mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-19 19:08:26 +00:00
Merge branch 'dev' into editgroup
This commit is contained in:
commit
f88558a719
@ -197,8 +197,8 @@ dependencies {
|
||||
implementation "com.opencsv:opencsv:$opencsv_version"
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 56
|
||||
def canonicalVersionName = "1.2.3"
|
||||
def canonicalVersionCode = 65
|
||||
def canonicalVersionName = "1.4.1"
|
||||
|
||||
def postFixSize = 10
|
||||
def abiPostFix = ['armeabi-v7a' : 1,
|
||||
|
@ -25,7 +25,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginTop="7dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
@ -36,8 +36,10 @@
|
||||
android:id="@+id/displayNameEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="28dp"
|
||||
android:paddingBottom="28dp"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:inputType="textCapWords"
|
||||
android:hint="@string/activity_display_name_edit_text_hint" />
|
||||
|
@ -57,6 +57,7 @@
|
||||
android:layout_height="@dimen/onboarding_button_bottom_offset"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:visibility="invisible"
|
||||
android:gravity="center"
|
||||
android:background="@color/transparent"
|
||||
android:textAllCaps="false"
|
||||
|
@ -1,114 +1,116 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/very_large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="Message Notifications" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="There are two ways Session can notify you of new messages." />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fcmOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/very_large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_explanation" />
|
||||
android:textStyle="bold"
|
||||
android:text="Fast Mode" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fcmOptionView"
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="You’ll be notified of new messages reliably and immediately using Google’s notification servers. The contents of your messages, and who you’re messaging, are never exposed to Google." />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_fcm_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_fcm_option_explanation" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/accent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_recommended_option_tag" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/backgroundPollingOptionView"
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_background_polling_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_background_polling_option_explanation" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentFilledButton"
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="@string/continue_2" />
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textColor="@color/accent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_recommended_option_tag" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
<LinearLayout
|
||||
android:id="@+id/backgroundPollingOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="Slow Mode" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="Session will occasionally check for new messages in the background. Full metadata protection is guaranteed, but message notifications will be unreliable." />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentFilledButton"
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:layout_marginBottom="@dimen/onboarding_button_bottom_offset"
|
||||
android:text="@string/continue_2" />
|
||||
|
||||
</LinearLayout>
|
@ -25,7 +25,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginTop="7dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
@ -36,8 +36,10 @@
|
||||
android:id="@+id/mnemonicEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="28dp"
|
||||
android:paddingBottom="28dp"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:hint="@string/activity_restore_seed_edit_text_hint" />
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/very_large_font_size"
|
||||
android:textSize="26sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_seed_title_2" />
|
||||
@ -31,9 +31,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_seed_explanation" />
|
||||
|
||||
@ -43,10 +43,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textSize="16sp"
|
||||
android:textAlignment="center"
|
||||
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:text="@string/activity_seed_reveal_button_title" />
|
||||
|
@ -28,19 +28,7 @@
|
||||
android:id="@+id/joinPublicChatButton"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:text="Next" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:textAlignment="center"
|
||||
android:text="Open groups can be joined by anyone and do not provide full privacy protection" />
|
||||
android:text="@string/next" />
|
||||
|
||||
</LinearLayout>
|
@ -12,10 +12,16 @@
|
||||
style="@style/SessionEditText"
|
||||
android:id="@+id/publicKeyEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginLeft="@dimen/large_spacing"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:paddingTop="0dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="2"
|
||||
android:imeOptions="actionDone"
|
||||
android:hint="@string/fragment_enter_public_key_edit_text_hint" />
|
||||
|
||||
<TextView
|
||||
@ -24,7 +30,7 @@
|
||||
android:layout_marginLeft="@dimen/large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:textAlignment="center"
|
||||
@ -73,7 +79,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:text="@string/share" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingLeft="@dimen/very_large_spacing"
|
||||
android:paddingTop="@dimen/large_spacing"
|
||||
android:paddingRight="@dimen/very_large_spacing"
|
||||
android:paddingBottom="@dimen/very_large_spacing"
|
||||
app:behavior_hideable="true"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
android:background="@drawable/default_bottom_sheet_background_inset">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Changes to Multi-Device"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:textSize="@dimen/very_large_font_size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/explanationTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:text="You’re seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on %s.\n\nTo read more about this change, visit the Session FAQ at getsession.org/faq"
|
||||
android:textColor="@color/text"
|
||||
android:textSize="@dimen/small_font_size" />
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentOutlineButton"
|
||||
android:id="@+id/okButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:text="@string/ok" />
|
||||
|
||||
</LinearLayout>
|
@ -1,118 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
app:behavior_hideable="true"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
android:background="@drawable/default_bottom_sheet_background_inset">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/very_large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_explanation" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fcmOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/sheet_pn_mode_fcm_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_fcm_option_explanation" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/accent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/sheet_pn_mode_recommended_option_tag" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/backgroundPollingOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/sheet_pn_mode_background_polling_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_background_polling_option_explanation" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentOutlineButton"
|
||||
android:id="@+id/confirmButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:text="@string/sheet_pn_mode_confirm_button_title" />
|
||||
|
||||
<Button
|
||||
style="@style/MediumUnimportantOutlineButton"
|
||||
android:id="@+id/skipButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="@string/sheet_pn_mode_skip_button_title" />
|
||||
|
||||
</LinearLayout>
|
@ -40,11 +40,12 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitleTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="260dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="@color/text"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:lines="2"
|
||||
android:alpha="0.6"
|
||||
android:text="Secure your account by saving your recovery phrase" />
|
||||
|
||||
|
@ -57,6 +57,7 @@
|
||||
android:layout_height="@dimen/onboarding_button_bottom_offset"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:visibility="invisible"
|
||||
android:gravity="center"
|
||||
android:background="@color/transparent"
|
||||
android:textAllCaps="false"
|
||||
|
@ -1,114 +1,116 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="Message Notifications" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="There are two ways Session can notify you of new messages." />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fcmOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_title" />
|
||||
android:text="Fast Mode" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_explanation" />
|
||||
android:text="You’ll be notified of new messages reliably and immediately using Google’s notification servers. The contents of your messages, and who you’re messaging, are never exposed to Google." />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fcmOptionView"
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_spacing"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_fcm_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_fcm_option_explanation" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/accent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_recommended_option_tag" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/backgroundPollingOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_spacing"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_background_polling_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/activity_pn_mode_background_polling_option_explanation" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentFilledButton"
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="@string/continue_2" />
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textColor="@color/accent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/activity_pn_mode_recommended_option_tag" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
<LinearLayout
|
||||
android:id="@+id/backgroundPollingOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="Slow Mode" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="Session will occasionally check for new messages in the background. Full metadata protection is guaranteed, but message notifications will be unreliable." />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentFilledButton"
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:layout_marginBottom="@dimen/onboarding_button_bottom_offset"
|
||||
android:text="@string/continue_2" />
|
||||
|
||||
</LinearLayout>
|
@ -46,7 +46,7 @@
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textSize="14sp"
|
||||
android:textAlignment="center"
|
||||
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
|
||||
|
||||
@ -59,6 +59,7 @@
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:visibility="gone"
|
||||
android:text="@string/activity_seed_reveal_button_title" />
|
||||
|
||||
<View
|
||||
|
@ -150,7 +150,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:text="@string/share" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -96,6 +96,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
android:layout_weight="1"
|
||||
android:textColorHint="#99FFFFFF"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
@ -137,7 +138,7 @@
|
||||
android:id="@+id/recorder_view"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_gravity="center"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
|
@ -208,12 +208,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.loki.views.FriendRequestView
|
||||
android:id="@+id/friend_request_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.components.AlertView
|
||||
@ -225,4 +219,5 @@
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</org.thoughtcrime.securesms.conversation.ConversationItem>
|
||||
|
||||
</org.thoughtcrime.securesms.conversation.ConversationItem>
|
@ -161,12 +161,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.loki.views.FriendRequestView
|
||||
android:id="@+id/friend_request_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.components.AlertView
|
||||
@ -181,4 +175,4 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</org.thoughtcrime.securesms.conversation.ConversationItem>
|
||||
</org.thoughtcrime.securesms.conversation.ConversationItem>
|
@ -49,7 +49,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/small_button_height"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:text="@string/delete" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -87,7 +87,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/small_button_height"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:text="@string/dialog_link_device_master_mode_authorize_button_title"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
android:text="@string/dialog_seed_title"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/medium_font_size" />
|
||||
|
||||
<TextView
|
||||
@ -59,7 +60,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/small_button_height"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:text="@string/copy" />
|
||||
|
||||
</LinearLayout>
|
||||
|
28
res/layout/fragment_conversation_bottom_sheet.xml
Normal file
28
res/layout/fragment_conversation_bottom_sheet.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:behavior_hideable="true"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
android:background="@color/dialog_background">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/blockOrUnblockTextView"
|
||||
style="@style/ActionItem"
|
||||
android:drawableStart="@drawable/ic_block_white_24dp"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/RecipientPreferenceActivity_block"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deleteTextView"
|
||||
style="@style/ActionItem"
|
||||
android:drawableStart="@drawable/ic_delete_white_24dp"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/delete" />
|
||||
|
||||
</LinearLayout>
|
@ -28,19 +28,7 @@
|
||||
android:id="@+id/joinPublicChatButton"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="@string/next" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:layout_marginBottom="@dimen/small_spacing"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/fragment_enter_chat_url_privacy_warning" />
|
||||
|
||||
</LinearLayout>
|
@ -12,10 +12,16 @@
|
||||
style="@style/SmallSessionEditText"
|
||||
android:id="@+id/publicKeyEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="54dp"
|
||||
android:layout_marginLeft="@dimen/large_spacing"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:paddingTop="0dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:inputType="textMultiLine"
|
||||
android:maxLines="2"
|
||||
android:imeOptions="actionDone"
|
||||
android:hint="@string/fragment_enter_public_key_edit_text_hint" />
|
||||
|
||||
<TextView
|
||||
@ -24,7 +30,7 @@
|
||||
android:layout_marginLeft="@dimen/large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/large_spacing"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:textAlignment="center"
|
||||
@ -73,7 +79,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:text="@string/share" />
|
||||
|
||||
</LinearLayout>
|
||||
|
42
res/layout/fragment_multi_device_removal_bottom_sheet.xml
Normal file
42
res/layout/fragment_multi_device_removal_bottom_sheet.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingLeft="@dimen/very_large_spacing"
|
||||
android:paddingTop="@dimen/large_spacing"
|
||||
android:paddingRight="@dimen/very_large_spacing"
|
||||
android:paddingBottom="@dimen/very_large_spacing"
|
||||
app:behavior_hideable="true"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
android:background="@drawable/default_bottom_sheet_background_inset">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Changes to Multi-Device"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:textSize="@dimen/large_font_size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/explanationTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:text="You’re seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on %s.\n\nTo read more about this change, visit the Session FAQ at getsession.org/faq"
|
||||
android:textColor="@color/text"
|
||||
android:textSize="@dimen/small_font_size" />
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentOutlineButton"
|
||||
android:id="@+id/okButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/large_spacing"
|
||||
android:text="@string/ok" />
|
||||
|
||||
</LinearLayout>
|
@ -1,118 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
app:behavior_hideable="true"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
android:background="@drawable/default_bottom_sheet_background_inset">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_explanation" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fcmOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/sheet_pn_mode_fcm_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_fcm_option_explanation" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/accent"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/sheet_pn_mode_recommended_option_tag" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/backgroundPollingOptionView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/pn_option_background">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/sheet_pn_mode_background_polling_option_title" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="@dimen/very_small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="@string/sheet_pn_mode_background_polling_option_explanation" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentOutlineButton"
|
||||
android:id="@+id/confirmButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/sheet_pn_mode_confirm_button_title" />
|
||||
|
||||
<Button
|
||||
style="@style/MediumUnimportantOutlineButton"
|
||||
android:id="@+id/skipButton"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginTop="@dimen/small_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="@string/sheet_pn_mode_skip_button_title" />
|
||||
|
||||
</LinearLayout>
|
@ -8,11 +8,11 @@
|
||||
android:id="@+id/quick_audio_toggle"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@null"
|
||||
android:contentDescription="@string/conversation_activity__quick_attachment_drawer_record_and_send_audio_description"
|
||||
android:scaleType="centerInside"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:tint="@color/text"
|
||||
app:srcCompat="@drawable/ic_microphone" />
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/unreadMessagesIndicatorView"
|
||||
android:id="@+id/accentView"
|
||||
android:layout_width="@dimen/accent_line_thickness"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/accent" />
|
||||
@ -18,14 +18,14 @@
|
||||
android:layout_width="@dimen/medium_profile_picture_size"
|
||||
android:layout_height="@dimen/medium_profile_picture_size"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:layout_marginBottom="@dimen/medium_spacing" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:layout_marginEnd="@dimen/medium_spacing"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
@ -54,7 +54,7 @@
|
||||
android:id="@+id/timestampTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/medium_spacing"
|
||||
android:layout_marginStart="@dimen/medium_spacing"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
@ -76,7 +76,7 @@
|
||||
android:layout_height="12dp"
|
||||
android:src="@drawable/ic_mute"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginRight="4dp" />
|
||||
android:layout_marginEnd="4dp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
@ -110,7 +110,7 @@
|
||||
android:id="@+id/statusIndicatorImageView"
|
||||
android:layout_width="@dimen/conversation_view_status_indicator_size"
|
||||
android:layout_height="@dimen/conversation_view_status_indicator_size"
|
||||
android:layout_marginLeft="@dimen/medium_spacing" />
|
||||
android:layout_marginStart="@dimen/medium_spacing" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
8
res/menu/conversation_block.xml
Normal file
8
res/menu/conversation_block.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/recipient_preferences__block"
|
||||
android:id="@+id/menu_block"
|
||||
android:icon="@drawable/ic_block_white_24dp" />
|
||||
|
||||
</menu>
|
8
res/menu/conversation_unblock.xml
Normal file
8
res/menu/conversation_unblock.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/ConversationActivity_unblock"
|
||||
android:id="@+id/menu_unblock"
|
||||
android:icon="@drawable/ic_check_white_24dp" />
|
||||
|
||||
</menu>
|
11
res/menu/menu_pn_mode.xml
Normal file
11
res/menu/menu_pn_mode.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:title="Learn More"
|
||||
android:id="@+id/learnMoreButton"
|
||||
android:icon="@drawable/ic_info_outline_white_24dp"
|
||||
app:showAsAction="always" />
|
||||
|
||||
</menu>
|
@ -1288,5 +1288,185 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">ZUM ENTSPERREN ANTIPPEN</string>
|
||||
<string name="RegistrationLockDialog_reminder">Erinnerung:</string>
|
||||
<string name="recipient_preferences__about">Über</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<string name="continue_2">Fortsetzen</string>
|
||||
<string name="copy">Kopieren</string>
|
||||
<string name="invalid_url">Ungültige URL</string>
|
||||
<string name="copied_to_clipboard">In die Zwischenablage kopiert.</string>
|
||||
<string name="device_linking_failed">Das Gerät konnte nicht verbunden werden.</string>
|
||||
<string name="next">Weiter</string>
|
||||
<string name="share">Teilen</string>
|
||||
<string name="invalid_session_id">Ungültige Session ID</string>
|
||||
<string name="cancel">Abbrechen</string>
|
||||
<string name="your_session_id">Ihre Session ID</string>
|
||||
|
||||
<string name="activity_landing_title_2">Ihre Session beginnt hier...</string>
|
||||
<string name="activity_landing_register_button_title">Session ID erstellen</string>
|
||||
<string name="activity_landing_restore_button_title">Ihre Session fortsetzen</string>
|
||||
<string name="activity_landing_link_button_title">Mit einem bestehenden Konto verlinken</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">Ihr Gerät wurde erfolgreich getrennt.</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">Was ist Session?</string>
|
||||
<string name="view_fake_chat_bubble_2">Es ist eine dezentrale, verschlüsselte Messaging-App.</string>
|
||||
<string name="view_fake_chat_bubble_3">Es werden also weder meine persönlichen Daten noch die Metadaten meiner Konversation erfasst? Wie funktioniert das?</string>
|
||||
<string name="view_fake_chat_bubble_4">Mit einer Kombination fortschrittlicher anonyme Routing- und End-to-End-Verschlüsselungstechnologien.</string>
|
||||
<string name="view_fake_chat_bubble_5">Freunde lassen Freunde keine kompromittierten Messenger verwenden. Herzlich Willkommen.</string>
|
||||
|
||||
<string name="activity_register_title">Das ist Ihre Session ID.</string>
|
||||
<string name="activity_register_explanation">Ihre Session ID ist die eindeutige Adresse, unter der Personen Sie über Session kontaktieren können. Ihre Session ID ist nicht mit Ihrer realen Identität verbunden, völlig anonym und von Natur aus privat.</string>
|
||||
<string name="activity_register_public_key_copied_message">In die Zwischenablage kopiert.</string>
|
||||
|
||||
<string name="activity_restore_title">Ihr Konto wiederherstellen</string>
|
||||
<string name="activity_restore_explanation">Geben Sie den Wiederherstellungssatz ein, den Sie bei der Anmeldung zur Wiederherstellung Ihres Kontos erhalten haben.</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">Ihr Wiederherstellungssatz</string>
|
||||
|
||||
<string name="activity_link_device_title">Gerät verbinden</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">Session ID eingeben</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">QR-Code scannen</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">Navigieren Sie zu "Einstellungen" > "Geräte" > "Ihr Gerät verknüpfen" und scannen Sie den angezeigten QR-Code, um den Verknüpfungsprozess zu starten.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">Ihr Gerät verknüpfen</string>
|
||||
<string name="fragment_enter_session_id_explanation">Navigieren Sie zu "Einstellungen" > "Geräte" > "Ihr Gerät verknüpfen" und geben Sie Ihre Session ID hier ein, um den Verknüpfungsprozess zu starten.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">Geben Sie Ihre Session ID ein.</string>
|
||||
|
||||
<string name="activity_display_name_title_2">Wählen Sie Ihren Anzeigenamen</string>
|
||||
<string name="activity_display_name_explanation">Dies ist Ihr Name, wenn Sie Session verwenden. Es kann Ihr richtiger Name, ein Alias oder etwas andere sein.</string>
|
||||
<string name="activity_display_name_edit_text_hint">Geben Sie einen Anzeigenamen ein</string>
|
||||
<string name="activity_display_name_display_name_missing_error">Bitte wählen Sie einen Anzeigenamen</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">Bitte wählen Sie einen Anzeigenamen, der nur aus den Zeichen a - z, A - Z, 0 - 9 und _ besteht.</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Bitte wählen Sie einen kürzeren Anzeigenamen</string>
|
||||
|
||||
<string name="activity_pn_mode_recommended_option_tag">Empfohlen</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Bitte wählen Sie eine Option aus.</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">Sie haben noch keine Kontakte.</string>
|
||||
<string name="activity_home_empty_state_button_title">Session starten</string>
|
||||
<string name="activity_home_leave_group_dialog_message">Sind Sie sich sicher, dass Sie diese Gruppe verlassen möchten?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">Gruppe konnte nicht verlassen werden.</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">Möchten Sie diese Unterhaltung wirklich löschen?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Die Unterhaltung wurde gelöscht.</string>
|
||||
|
||||
<string name="activity_seed_title">Ihr Wiederherstellungssatz</string>
|
||||
<string name="activity_seed_title_2">Das ist Ihr Wiederherstellungssatz.</string>
|
||||
<string name="activity_seed_explanation">Ihr Wiederherstellungssatz ist der Hauptschlüssel für Ihre Session ID. Mit diesem Satz können Sie Ihre Session ID wiederherstellen, wenn Sie den Zugriff auf Ihr Gerät verlieren. Bewahren Sie Ihren Wiederherstellungssatz an einem sicheren Ort auf und geben Sie ihn an niemandem weiter.</string>
|
||||
<string name="activity_seed_reveal_button_title">Zur Anzeige gedrückt halten</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">Sichern Sie Ihr Konto, indem Sie Ihren Wiederherstellungssatz speichern</string>
|
||||
<string name="view_seed_reminder_subtitle_2">Tippen und halten Sie die verborgenen Wörter, um Ihren Wiederherstellungssatz anzuzeigen, und speichern Sie ihn dann sicher, um Ihre Session ID zu sichern.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">Bewahren Sie Ihren Wiederherstellungssatz an einem sicheren Ort auf.</string>
|
||||
|
||||
<string name="activity_path_title">Pfad</string>
|
||||
<string name="activity_path_explanation">Session verbirgt Ihre IP-Adresse, indem Ihre Nachrichten über mehrere Dienstknoten im dezentralen Session-Netzwerk weitergeleitet werden. Dies sind die Länder, durch die Ihre Verbindung derzeit weitergeleitet wird:</string>
|
||||
<string name="activity_path_device_row_title">Sie</string>
|
||||
<string name="activity_path_guard_node_row_title">Eingangsknoten</string>
|
||||
<string name="activity_path_service_node_row_title">Dienstknoten</string>
|
||||
<string name="activity_path_destination_row_title">Ziel</string>
|
||||
<string name="activity_path_learn_more_button_title">Mehr erfahren</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">Neue Session</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">Session ID eingeben</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">QR-Code scannen</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">Scannen Sie den QR-Code eines Benutzers, um eine Session zu starten. QR-Codes finden Sie, indem Sie in den Einstellungen auf das QR-Code-Symbol tippen.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">Geben Sie eine Session ID ein.</string>
|
||||
<string name="fragment_enter_public_key_explanation">Benutzer können ihre Session ID freigeben, indem sie in ihren Einstellungen auf "Session ID freigeben" tippen oder ihren QR-Code freigeben.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">Session benötigt Kamerazugriff, um die QR-Codes scannen zu können.</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Kamerazugriff gewähren</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">Neue geschlossene Gruppe</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">Geben Sie einen Gruppennamen ein.</string>
|
||||
<string name="activity_create_closed_group_explanation">Geschlossene Gruppen unterstützen bis zu 10 Mitglieder und bieten den gleichen Schutz der Privatsphäre wie Einzelgespräche.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">Sie haben noch keine Kontakte.</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">Session starten</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">Bitte geben Sie einen Gruppennamen ein.</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">Bitte geben Sie einen kürzeren Gruppennamen ein.</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">Bitte wählen Sie mindestens zwei Gruppenmitglieder aus.</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">Eine geschlossene Gruppe kann maximal zehn Mitglieder haben.</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">Ein Mitglied Ihrer Gruppe hat eine ungültige Session ID.</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">Offener Gruppe beitreten</string>
|
||||
<string name="activity_join_public_chat_error">Konnte der Gruppe nicht beitreten.</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">Gruppen-URL öffnen</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">QR-Code scannen</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Scannen Sie den QR-Code der offenen Gruppe, der Sie beitreten möchten.</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Geben Sie eine offene Gruppen-URL ein.</string>
|
||||
|
||||
<string name="activity_settings_title">Einstellungen</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Geben Sie einen Anzeigenamen ein.</string>
|
||||
<string name="activity_settings_display_name_missing_error">Bitte wählen Sie einen Anzeigenamen.</string>
|
||||
<string name="activity_settings_invalid_display_name_error">Bitte wählen Sie einen Anzeigenamen, der nur aus den Zeichen a - z, A - Z, 0 - 9 und _ besteht.</string>
|
||||
<string name="activity_settings_display_name_too_long_error">Bitte wählen Sie einen kürzeren Anzeigenamen.</string>
|
||||
<string name="activity_settings_privacy_button_title">Datenschutz</string>
|
||||
<string name="activity_settings_notifications_button_title">Benachrichtigungen</string>
|
||||
<string name="activity_settings_chats_button_title">Chats</string>
|
||||
<string name="activity_settings_devices_button_title">Geräte</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">Wiederherstellungssatz</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">Daten löschen</string>
|
||||
|
||||
<string name="activity_notification_settings_title">Benachrichtigungen</string>
|
||||
<string name="activity_notification_settings_style_section_title">Stil der Benachrichtigungen</string>
|
||||
<string name="activity_notification_settings_content_section_title">Inhalt der Benachrichtigungen</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">Datenschutz</string>
|
||||
|
||||
<string name="activity_chat_settings_title">Chats</string>
|
||||
|
||||
<string name="activity_linked_devices_title">Geräte</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">Gerätelimit erreicht</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">Es ist derzeit nicht erlaubt, mehr als ein Gerät zu verbinden.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">Gerät konnte nicht getrennt werden.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">Ihr Gerät wurde erfolgreich getrennt.</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">Das Gerät konnte nicht verbunden werden.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">Sie haben noch keine Geräte verlinkt.</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">Gerät (Beta) verknüpfen</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Benachrichtigungsstrategie</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">Warten auf Autorisierung</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Geräteverbindung autorisiert</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">Bitte überprüfen Sie, ob die folgenden Wörter mit denen auf Ihrem anderen Gerät übereinstimmen.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">Ihr Gerät wurde erfolgreich verknüpft.</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">Warten auf Gerät</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">Verknüpfungsanfrage empfangen</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">Gerätelink wird autorisiert</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">Laden Sie Session auf Ihr anderes Gerät herunter und tippen Sie unten auf dem Startbildschirm auf "Mit einem bestehenden Konto verlinken". Wenn Sie bereits ein Konto auf Ihrem anderen Gerät haben, müssen Sie dieses Konto zuerst löschen.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">Bitte überprüfen Sie, ob die folgenden Wörter mit denen auf Ihrem anderen Gerät übereinstimmen.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">Bitte warten Sie, während der Gerätelink erstellt wird. Dies kann bis zu einer Minute dauern.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">Autorisieren</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">Namen ändern</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">Gerät trennen</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">Geben Sie einen Namen ein.</string>
|
||||
|
||||
<string name="dialog_seed_title">Ihr Wiederherstellungssatz</string>
|
||||
<string name="dialog_seed_explanation">Das ist Ihr Wiederherstellungssatz. Damit können Sie Ihre Session ID wiederherstellen oder auf ein neues Gerät migrieren.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">Alle Daten löschen</string>
|
||||
<string name="dialog_clear_all_data_explanation">Dadurch werden Ihre Nachrichten, Sessions und Kontakte dauerhaft gelöscht.</string>
|
||||
|
||||
<string name="activity_qr_code_title">QR-Code</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">Meinen QR-Code anzeigen</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">QR-Code scannen</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">Scannen Sie den QR-Code einer Person, um ein Gespräch mit ihr zu beginnen.</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">Das ist Ihr QR-Code. Andere Benutzer können ihn scannen, um eine Session mit Ihnen zu starten.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">QR-Code freigeben</string>
|
||||
|
||||
<string name="session_reset_banner_message">Möchten Sie Ihre Session mit %s wiederherstellen?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Verwerfen</string>
|
||||
<string name="session_reset_banner_restore_button_title">Wiederherstellen</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">Kontakte</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Geschlossene Gruppen</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Gruppen öffnen</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1353,12 +1353,6 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="activity_display_name_display_name_invalid_error">Por favor, elige un nombre para mostrar que contenga solo caracteres a-z, A-Z, 0-9 y _</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Por favor, elige un nombre para mostrar más corto</string>
|
||||
|
||||
<string name="activity_pn_mode_title">Notificaciones Push</string>
|
||||
<string name="activity_pn_mode_explanation">Session tiene dos tipos de notificaciones push. Asegúrate de leer cuidadosamente las descripciones antes de elegir.</string>
|
||||
<string name="activity_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||
<string name="activity_pn_mode_fcm_option_explanation">Session usará el servicio Firebase Cloud Messaging para recibir notificaciones push. Recibirás notificaciones de nuevos mensajes de manera segura e inmediata. Usar FCM significa que tu dirección IP y device token serán compartidos con Google. Este sería ya el caso si recibes notificaciones push con otras aplicaciones. Tu dirección IP y device token serán compartidos con Loki, pero tus mensajes seguirán teniendo enrutamiento cebolla y encriptación de extremo a extremo, por lo que el contenido de tus mensajes seguirá siendo completamente privado.</string>
|
||||
<string name="activity_pn_mode_background_polling_option_title">Sondeo en segundo plano</string>
|
||||
<string name="activity_pn_mode_background_polling_option_explanation">Session revisará si hay nuevos mensajes en segundo plano y de manera ocasional. Esto garantiza una protección total de la privacidad, pero las notificaciones de mensajes pueden retrasarse significativamente.</string>
|
||||
<string name="activity_pn_mode_recommended_option_tag">Recomendado</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Por favor, elige una opción</string>
|
||||
|
||||
@ -1369,17 +1363,6 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="activity_home_delete_conversation_dialog_message">¿Seguro que quieres eliminar esta conversación?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Conversación eliminada</string>
|
||||
|
||||
<string name="sheet_pn_mode_title">Notificaciones Push</string>
|
||||
<string name="sheet_pn_mode_explanation">Session ahora tiene dos formas de manejar las notificaciones push. Asegúrate de leer las descripciones cuidadosamente antes de elegir.</string>
|
||||
<string name="sheet_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||
<string name="sheet_pn_mode_fcm_option_explanation">Session usará el servicio Firebase Cloud Messaging para recibir las notificaciones push. Recibirás notificaciones de nuevos mensajes de manera confiable e inmediata. Usar FCM significa que este dispositivo se comunicará directamente con los servidores de Google para recuperar las notificaciones push, lo que expondrá tu dirección IP a Google. A tus mensajes se les seguirá realizando enrutamiento cebolla y cifrado de extremo a extremo, por lo que el contenido de tus mensajes permanecerá completamente privado.</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_title">Sondeo en segundo plano</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_explanation">Session revisará si hay nuevos mensajes en segundo plano y de manera ocasional. Esto garantiza una protección total de los metadatos, pero las notificaciónes de nuevos mensajes pueden retrasarse significativamente.</string>
|
||||
<string name="sheet_pn_mode_recommended_option_tag">Recomendado</string>
|
||||
<string name="sheet_pn_mode_no_option_picked_dialog_title">Por favor, elige una opción</string>
|
||||
<string name="sheet_pn_mode_confirm_button_title">Confirmar</string>
|
||||
<string name="sheet_pn_mode_skip_button_title">Omitir</string>
|
||||
|
||||
<string name="activity_seed_title">Tu frase de recuperación</string>
|
||||
<string name="activity_seed_title_2">Guarda tu frase de recuperación</string>
|
||||
<string name="activity_seed_explanation">Tu frase de recuperación es la llave maestra de tu ID de Session, puedes usarla para recuperar tu ID de Session en caso de pérdida de acceso a tu dispositivo. Guarda tu frase de recuperación en un lugar seguro y no se la digas a nadie.</string>
|
||||
@ -1426,7 +1409,6 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Escanea el código QR del grupo abierto al que quieras unirte</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Ingresa una URL de grupo abierto</string>
|
||||
<string name="fragment_enter_chat_url_privacy_warning">Cualquiera puede unirse a los grupos abiertos. Esto no brinda una protección completa de privacidad</string>
|
||||
|
||||
<string name="activity_settings_title">Ajustes</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Ingresa un nombre para mostrar</string>
|
||||
@ -1458,8 +1440,6 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="activity_linked_devices_empty_state_button_title">Enlazar un dispositivo</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Estrategia de notificación</string>
|
||||
<string name="preferences_notifications_use_fcm_option_title">Utilizar FCM</string>
|
||||
<string name="preferences_notifications_use_fcm_option_explanation">El uso de Firebase Cloud Messaging permite notificaciones push más seguras, pero expone tu IP a Google.</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">Esperando la autorización</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Vinculación de dispositivo autorizada</string>
|
||||
@ -1493,16 +1473,6 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="fragment_view_my_qr_code_explanation">Este es tu código QR. Otros usuarios pueden escanearlo para empezar una Session contigo.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Compartir código QR</string>
|
||||
|
||||
<string name="view_friend_request_accept_button_title">Aceptar</string>
|
||||
<string name="view_friend_request_reject_button_title">Rechazar</string>
|
||||
<string name="view_friend_request_incoming_pending_message">%1$s te envió una solicitud de Session</string>
|
||||
<string name="view_friend_request_incoming_accepted_message">Has aceptado la solicitud de Session de %1$s</string>
|
||||
<string name="view_friend_request_incoming_declined_message">Has rechazado la solicitud de Session de %1$s</string>
|
||||
<string name="view_friend_request_incoming_expired_message">La solicitud de Session de %1$s ha expirado</string>
|
||||
<string name="view_friend_request_outgoing_pending_message">Le has enviado una solicitud de Session a %1$s</string>
|
||||
<string name="view_friend_request_outgoing_accepted_message">%1$s aceptó tu solicitud de Session</string>
|
||||
<string name="view_friend_request_outgoing_expired_message">Tu solicitud de Session para %1$s ha expirado</string>
|
||||
|
||||
<string name="session_reset_banner_message">¿Quieres restaurar tu Session con %s?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Descartar</string>
|
||||
<string name="session_reset_banner_restore_button_title">Restaurar</string>
|
||||
@ -1510,4 +1480,5 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
|
||||
<string name="fragment_contact_selection_contacts_title">Contactos</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Grupos cerrados</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Grupos abiertos</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1214,5 +1214,217 @@
|
||||
<string name="prompt_passphrase_activity__signal_is_locked">Session قفل شده است</string>
|
||||
<string name="RegistrationLockDialog_reminder">یادآور:</string>
|
||||
<string name="recipient_preferences__about">درباره ی ما</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<!--
|
||||
<string name="continue_2">ادامه </string>
|
||||
<string name="copy">رونویسی کردن</string>
|
||||
<string name="invalid_url">URL نامعتبر است</string>
|
||||
<string name="copied_to_clipboard">در کلیپ بورد کپی شد</string>
|
||||
<string name="device_linking_failed">پیوند دستگاه امکان پذیر نیست.</string>
|
||||
<string name="next">بعد</string>
|
||||
<string name="share">اشتراک گذاری</string>
|
||||
<string name="invalid_session_id">شناسه نامعتبر جلسه</string>
|
||||
<string name="cancel">لغو</string>
|
||||
<string name="your_session_id">شناسه جلسه شما</string>
|
||||
|
||||
<string name="activity_landing_title_2">جلسه شما از اینجا شروع می شود...</string>
|
||||
<string name="activity_landing_register_button_title">شناسه جلسه را ایجاد کنید</string>
|
||||
<string name="activity_landing_restore_button_title">جلسه خود را ادامه دهید</string>
|
||||
<string name="activity_landing_link_button_title">پیوند به یک حساب کاربری موجود</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">ارتباط دستگاه شما با موفقیت انجام نشد</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">جلسه چیست؟</string>
|
||||
<string name="view_fake_chat_bubble_2">این یک برنامه پیام رسانی غیرمتمرکز و رمزگذاری شده است</string>
|
||||
<string name="view_fake_chat_bubble_3">بنابراین اطلاعات شخصی یا ابرداده گفتگوی من جمع نمی شود؟ چگونه کار می کند؟</string>
|
||||
<string name="view_fake_chat_bubble_4">استفاده از ترکیبی از فن آوری های رمزگذاری پیشرفته مسیریابی ناشناس و پایان به پایان.</string>
|
||||
<string name="view_fake_chat_bubble_5">دوستان اجازه نمی دهند تا دوستان از پیام رسان های مصالحه استفاده کنند. خواهش میکنم.</string>
|
||||
|
||||
<string name="activity_register_title">سلام به شناسه جلسه خود</string>
|
||||
<string name="activity_register_explanation">شناسه جلسه شما آدرس منحصر به فردی است که افراد می توانند از آنها برای تماس با شما در جلسه استفاده کنند. بدون ارتباط با هویت واقعی شما ، شناسه جلسه شما کاملاً ناشناس و خصوصی است.</string>
|
||||
<string name="activity_register_public_key_copied_message">در کلیپ بورد کپی شد</string>
|
||||
|
||||
<string name="activity_restore_title">حساب خود را بازیابی کنید</string>
|
||||
<string name="activity_restore_explanation">عبارت بازیابی را هنگام ثبت نام برای بازیابی حساب خود وارد کنید.</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">عبارت بازیابی خود را وارد کنید</string>
|
||||
|
||||
<string name="activity_link_device_title">دستگاه پیوند</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">شناسه جلسه را وارد کنید</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">اسکن کد QR</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">به تنظیمات > دستگاه ها > پیوند دستگاه در دستگاه دیگر خود بروید و سپس کد QR را که برای شروع کار پیوند داده است ، اسکن کنید.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">دستگاه خود را پیوند دهید</string>
|
||||
<string name="fragment_enter_session_id_explanation">به تنظیمات > دستگاه ها > پیوند دستگاه در دستگاه دیگر خود بروید و سپس شناسه جلسه خود را در اینجا وارد کنید تا فرایند پیوند آغاز شود.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">شناسه جلسه خود را وارد کنید</string>
|
||||
|
||||
<string name="activity_display_name_title_2">نام صفحه نمایش خود را انتخاب کنید</string>
|
||||
<string name="activity_display_name_explanation">این نام شما هنگام استفاده از جلسه خواهد بود. این می تواند نام واقعی شما باشد ، نام مستعار یا هر چیز دیگری که دوست دارید.</string>
|
||||
<string name="activity_display_name_edit_text_hint">نام نمایشگر را وارد کنید</string>
|
||||
<string name="activity_display_name_display_name_missing_error">لطفاً نام نمایشگر را انتخاب کنید</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">لطفاً یک نام نمایشگر را انتخاب کنید که فقط از نویسه های Az ، AZ ، 0-9 و _ تشکیل شده باشد</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">لطفاً نام نمایشگر کوتاه تری انتخاب کنید</string>
|
||||
|
||||
<string name="activity_pn_mode_title">اعلانهای فشار</string>
|
||||
<string name="activity_pn_mode_explanation">دو راه وجود دارد که جلسه می تواند اعلان های فشار را کنترل کند. حتما قبل از انتخاب توضیحات را با دقت بخوانید.</string>
|
||||
<string name="activity_pn_mode_fcm_option_title">پیام ابری فایربیس</string>
|
||||
<string name="activity_pn_mode_fcm_option_explanation">جلسه برای دریافت اعلان های فشار از سرویس پیام ابری فایربیس استفاده می کند. با اطمینان و بلافاصله از پیامهای جدید مطلع خواهید شد. استفاده از FCM بدان معنا است که آدرس IP و نشانه دستگاه شما در معرض گوگل قرار خواهد گرفت. اگر از اعلانهای فشار برای سایر برنامه ها استفاده می کنید ، این مورد در حال حاضر اینگونه خواهد بود. آدرس IP و نشانه دستگاه شما نیز در معرض Loki قرار خواهد گرفت ، اما پیام های شما هنوز هم از طریق رمزگذاری شده توسط اونیون و رمزگذاری نهایی به پایان می رسد ، بنابراین محتوای پیام های شما کاملاً خصوصی خواهد بود.</string>
|
||||
<string name="activity_pn_mode_background_polling_option_title">زمینه رای گیری</string>
|
||||
<string name="activity_pn_mode_background_polling_option_explanation">جلسه گاه به گاه پیام های جدید را در پس زمینه بررسی می کند. این محافظت کامل از ابرداده را تضمین می کند ، اما اعلان های پیام ممکن است به میزان قابل توجهی به تأخیر بیفتند.</string>
|
||||
<string name="activity_pn_mode_recommended_option_tag">توصیه شده</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">لطفا گزینه ای را انتخاب کنید</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">شما هنوز هیچ تماسی ندارید</string>
|
||||
<string name="activity_home_empty_state_button_title">شروع جلسه</string>
|
||||
<string name="activity_home_leave_group_dialog_message">آیا مطمئن هستید که می خواهید این گروه را ترک کنید؟</string>
|
||||
<string name="activity_home_leaving_group_failed_message">نمی توان گروه را ترک کرد</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">آیا مطمئن هستید که می خواهید این مکالمه را حذف کنید؟</string>
|
||||
<string name="activity_home_conversation_deleted_message">مکالمه حذف شد</string>
|
||||
|
||||
<string name="sheet_pn_mode_title">اعلانهای فشار</string>
|
||||
<string name="sheet_pn_mode_explanation">جلسه اکنون دو راه برای رسیدگی به اعلان های فشار دارد. حتما قبل از انتخاب توضیحات را با دقت بخوانید.</string>
|
||||
<string name="sheet_pn_mode_fcm_option_title">پیام ابری فایربیس</string>
|
||||
<string name="sheet_pn_mode_fcm_option_explanation">جلسه برای دریافت اعلان های فشار از سرویس پیام ابری فایربیس استفاده می کند. با اطمینان و بلافاصله از پیامهای جدید مطلع خواهید شد. استفاده از FCM بدان معنا است که آدرس IP و نشانه دستگاه شما در معرض گوگل قرار خواهد گرفت. اگر از اعلانهای فشار برای سایر برنامه ها استفاده می کنید ، این مورد در حال حاضر اینگونه خواهد بود. آدرس IP و نشانه دستگاه شما نیز در معرض Loki قرار خواهد گرفت ، اما پیام های شما هنوز هم از طریق رمزگذاری شده توسط پیاز و رمزگذاری نهایی به پایان می رسد ، بنابراین محتوای پیام های شما کاملاً خصوصی خواهد بود.</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_title">زمینه رای گیری</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_explanation">جلسه گاه به گاه پیام های جدید را در پس زمینه بررسی می کند. این محافظت کامل از ابرداده را تضمین می کند ، اما اعلان های پیام ممکن است به میزان قابل توجهی به تأخیر بیفتند.</string>
|
||||
<string name="sheet_pn_mode_recommended_option_tag">توصیه شده</string>
|
||||
<string name="sheet_pn_mode_no_option_picked_dialog_title">لطفا گزینه ای را انتخاب کنید</string>
|
||||
<string name="sheet_pn_mode_confirm_button_title">تایید</string>
|
||||
<string name="sheet_pn_mode_skip_button_title">رد</string>
|
||||
|
||||
<string name="activity_seed_title">عبارت بازیابی شما</string>
|
||||
<string name="activity_seed_title_2">با عبارت بازیابی خود مطابقت داشته باشید</string>
|
||||
<string name="activity_seed_explanation">عبارت بازیابی شما کلید اصلی شناسه جلسه شما است - در صورت عدم دسترسی به دستگاه خود می توانید از آن برای بازگرداندن شناسه جلسه استفاده کنید. عبارت بازیابی خود را در مکانی امن ذخیره کنید و آن را به کسی ندهید.</string>
|
||||
<string name="activity_seed_reveal_button_title">نگه دارید تا فاش شود</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">با ذخیره کردن عبارت بازیابی ، حساب خود را ایمن کنید</string>
|
||||
<string name="view_seed_reminder_subtitle_2">برای فاش کردن عبارت بازیابی ، بر روی کلمات redacted ضربه زده و نگه دارید ، سپس با خیال راحت آن را ذخیره کنید تا شناسه جلسه خود را ایمن نمایید.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">حتماً عبارت بازیابی خود را در مکانی امن ذخیره کنید</string>
|
||||
|
||||
<string name="activity_path_title">مسیر</string>
|
||||
<string name="activity_path_explanation">جلسه IP شما را با گزاف گویی پیام های خود از طریق چندین گره سرویس در شبکه غیرمتمرکز جلسه مخفی می کند. اینها کشورهایی هستند که اتصال شما در حال حاضر از طریق آن فراخوانی می شوند:</string>
|
||||
<string name="activity_path_device_row_title">شما</string>
|
||||
<string name="activity_path_guard_node_row_title">گره ورود</string>
|
||||
<string name="activity_path_service_node_row_title">گره سرویس</string>
|
||||
<string name="activity_path_destination_row_title">مقصد</string>
|
||||
<string name="activity_path_learn_more_button_title">بیشتر بدانید</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">جلسه جدید</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">شناسه جلسه را وارد کنید</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">اسکن کد QR</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">برای شروع جلسه ، کد QR کاربر را اسکن کنید. با ضربه زدن روی نماد کد QR در تنظیمات حساب ، کدهای QR را می توان یافت.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">شناسه گیرنده را وارد کنید</string>
|
||||
<string name="fragment_enter_public_key_explanation">کاربران می توانند با وارد کردن به تنظیمات حساب خود و ضربه زدن به Share Share Session ID یا با به اشتراک گذاشتن کد QR خود ، شناسه جلسه خود را به اشتراک بگذارند.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">جلسه برای اسکن کدهای QR به دوربین دسترسی دارد</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">دسترسی به کمک دوربین</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">گروه بسته شده جدید</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">نام گروه را وارد کنید</string>
|
||||
<string name="activity_create_closed_group_explanation">گروه های بسته تا 10 عضو را پشتیبانی می کنند و همان محافظت از حریم خصوصی را به عنوان جلسات یک به یک ارائه می دهند.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">شما هنوز هیچ تماسی ندارید</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">شروع جلسه</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">لطفاً یک نام گروه وارد کنید</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">لطفاً نام گروه کوتاه تری وارد کنید</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">لطفا حداقل 2 عضو گروه را انتخاب کنید</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">یک گروه بسته نمی تواند بیش از 10 عضو داشته باشد</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">یکی از اعضای گروه شما دارای شناسه نامعتبر است</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">به گروه باز بپیوندید</string>
|
||||
<string name="activity_join_public_chat_error">امکان پیوستن به گروه نیست</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">آدرس اینترنتی گروه را باز کنید</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">اسکن کد QR</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">کد QR گروه باز را که می خواهید بپیوندید اسکن کنید</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">یک URL گروه باز وارد کنید</string>
|
||||
<string name="fragment_enter_chat_url_privacy_warning">گروه های باز می توانند توسط هر کسی بپیوندند و محافظت کامل از حریم خصوصی ارائه نمی دهند</string>
|
||||
|
||||
<string name="activity_settings_title">تنظیمات</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">نام نمایشگر را وارد کنید</string>
|
||||
<string name="activity_settings_display_name_missing_error">لطفاً نام نمایشگر را انتخاب کنید</string>
|
||||
<string name="activity_settings_invalid_display_name_error">لطفاً یک نام نمایشگر را انتخاب کنید که فقط از نویسه های Az ، AZ ، 0-9 و _ تشکیل شده باشد</string>
|
||||
<string name="activity_settings_display_name_too_long_error">لطفاً نام نمایشگر کوتاه تری انتخاب کنید</string>
|
||||
<string name="activity_settings_privacy_button_title">حریم خصوصی</string>
|
||||
<string name="activity_settings_notifications_button_title">اطلاعیه</string>
|
||||
<string name="activity_settings_chats_button_title">چت</string>
|
||||
<string name="activity_settings_devices_button_title">دستگاه ها</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">عبارت بازیابی</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">اطلاعات روشن</string>
|
||||
|
||||
<string name="activity_notification_settings_title">اطلاعیه</string>
|
||||
<string name="activity_notification_settings_style_section_title">سبک اطلاع رسانی</string>
|
||||
<string name="activity_notification_settings_content_section_title">محتوای اطلاع رسانی</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">حریم خصوصی</string>
|
||||
|
||||
<string name="activity_chat_settings_title">چت</string>
|
||||
|
||||
<string name="activity_linked_devices_title">دستگاه ها</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">دستگاه محدود شد</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">در حال حاضر مجاز به پیوند بیش از یک دستگاه نیست.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">دستگاه را نمی توان ارتباط برقرار کرد.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">دستگاه شما با موفقیت ارتباط برقرار نشد</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">پیوند دستگاه امکان پذیر نیست.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">شما هنوز هیچ دستگاهی را پیوند نداده اید</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">پیوند یک دستگاه (بتا)</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">استراتژی اعلان</string>
|
||||
<string name="preferences_notifications_use_fcm_option_title">از FCM استفاده کنید</string>
|
||||
<string name="preferences_notifications_use_fcm_option_explanation">با استفاده از پیام ابری فایربیس، اعلان های فشار قابل اطمینان تر امکان پذیر است ، اما نشانگر IP و دستگاه شما را در گوگل و لوکی قرار می دهد.</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">در انتظار مجوز</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">پیوند دستگاه مجاز است</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">لطفاً بررسی کنید که کلمات زیر با کلمات نشان داده شده در دستگاه دیگر شما مطابقت دارند.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">دستگاه شما با موفقیت پیوند خورده است</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">منتظر دستگاه</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">درخواست پیوند دریافت شد</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">پیوند دستگاه مجاز</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">جلسه را در دستگاه دیگر خود دانلود کرده و روی پیوند به یک حساب موجود در پایین صفحه فرود ضربه بزنید. اگر در حال حاضر دارای یک حساب کاربری در دستگاه دیگر خود هستید ، ابتدا باید آن حساب را حذف کنید.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">لطفاً بررسی کنید که کلمات زیر با کلمات نشان داده شده در دستگاه دیگر شما مطابقت دارند.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">لطفاً منتظر بمانید تا پیوند دستگاه ایجاد شود. این ممکن است تا یک دقیقه طول بکشد.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">اجازه دهید</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">تغییر نام</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">دستگاه را جدا کنید</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">یک نام وارد کنید</string>
|
||||
|
||||
<string name="dialog_seed_title">عبارت بازیابی شما</string>
|
||||
<string name="dialog_seed_explanation">این عبارت بازیابی شماست. با استفاده از آن ، می توانید شناسه جلسه خود را به دستگاه جدید بازیابی یا انتقال دهید.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">پاک کردن همه داده ها</string>
|
||||
<string name="dialog_clear_all_data_explanation">این به طور دائم پیام ها، جلسات و مخاطبین شما را حذف می کند.</string>
|
||||
|
||||
<string name="activity_qr_code_title">کد QR</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">مشاهده کد QR من</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">اسکن کد QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">برای شروع مکالمه با آنها ، کد QR شخصی را اسکن کنید</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">این کد QR شماست. سایر کاربران می توانند برای شروع جلسه با شما آن را اسکن کنند.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">کد QR را به اشتراک بگذارید</string>
|
||||
|
||||
<string name="view_friend_request_accept_button_title">تایید کنید</string>
|
||||
<string name="view_friend_request_reject_button_title">کاهش می یابد</string>
|
||||
<string name="view_friend_request_incoming_pending_message">%1$s برای شما درخواست جلسه ارسال کرد</string>
|
||||
<string name="view_friend_request_incoming_accepted_message">شما قبول کرده اید %1$s درخواست جلسه</string>
|
||||
<string name="view_friend_request_incoming_declined_message">شما کاهش داده اید %1$s درخواست جلسه</string>
|
||||
<string name="view_friend_request_incoming_expired_message">%1$s درخواست جلسه منقضی شده است</string>
|
||||
<string name="view_friend_request_outgoing_pending_message">شما ارسال کرده اید %1$s درخواست جلسه</string>
|
||||
<string name="view_friend_request_outgoing_accepted_message">%1$s درخواست جلسه خود را پذیرفته اید</string>
|
||||
<string name="view_friend_request_outgoing_expired_message">درخواست جلسه شما به %1$s منقضی شده است</string>
|
||||
|
||||
<string name="session_reset_banner_message">آیا می خواهید جلسه خود را با آن بازیابی کنید %s ؟</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">رد</string>
|
||||
<string name="session_reset_banner_restore_button_title">بازگرداندن</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">مخاطبین</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">گروه های بسته</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">باز کردن گروه ها</string>
|
||||
-->
|
||||
|
||||
</resources>
|
||||
|
@ -1294,5 +1294,186 @@ Vous avez reçu un message d’échange de clés pour une version de protocole i
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">TOUCHEZ POUR DÉVERROUILLER</string>
|
||||
<string name="RegistrationLockDialog_reminder">Rappel :</string>
|
||||
<string name="recipient_preferences__about">À propos de Session</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<string name="continue_2">Continuer</string>
|
||||
<string name="copy">Copier</string>
|
||||
<string name="invalid_url">URL non valide</string>
|
||||
<string name="copied_to_clipboard">Copié dans le presse-papier</string>
|
||||
<string name="device_linking_failed">Impossible de relier l\'appareil.</string>
|
||||
<string name="next">Suivant</string>
|
||||
<string name="share">Partager</string>
|
||||
<string name="invalid_session_id">Session ID non valide</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="your_session_id">Votre Session ID</string>
|
||||
|
||||
<string name="activity_landing_title_2">Votre Session débute ici...</string>
|
||||
<string name="activity_landing_register_button_title">Créer un Session ID</string>
|
||||
<string name="activity_landing_restore_button_title">Continuez votre Session</string>
|
||||
<string name="activity_landing_link_button_title">Relier à un compte existant</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">Votre appareil a été déconnecté avec succès</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">Qu\'est-ce que Session ?</string>
|
||||
<string name="view_fake_chat_bubble_2">C\'est une application de messagerie décentralisée et cryptée</string>
|
||||
<string name="view_fake_chat_bubble_3">Elle ne recueille donc pas mes informations personnelles ou mes métadonnées de conversations ? Comment ça marche ?</string>
|
||||
<string name="view_fake_chat_bubble_4">En utilisant une combinaison de technologies avancées de routage anonyme et de chiffrement de bout en bout.</string>
|
||||
<string name="view_fake_chat_bubble_5">Les vrais amis ne laissent pas leurs amis utiliser des outils de messagerie compromis. De rien.</string>
|
||||
|
||||
<string name="activity_register_title">Dites bonjour à votre Session ID</string>
|
||||
<string name="activity_register_explanation">Votre Session ID est l\'identifiant unique que les gens utilisent pour vous contacter dans Session. Sans lien avec votre identité réelle, votre Session ID est complètement anonyme et privé.</string>
|
||||
<string name="activity_register_public_key_copied_message">Copié dans le presse-papier</string>
|
||||
|
||||
<string name="activity_restore_title">Restaurez votre compte</string>
|
||||
<string name="activity_restore_explanation">Pour restaurer votre compte, veuillez entrer la phrase de récupération qui vous a été fournie lors de la création de votre compte.</string>
|
||||
|
||||
<string name="activity_restore_seed_edit_text_hint">Saisissez votre phrase de récupération</string>
|
||||
|
||||
<string name="activity_link_device_title">Relier un appareil</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">Saisir un Session ID</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">Scanner un code QR</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">Rendez-vous dans Paramètres > Appareils reliés > Relier un appareil sur votre autre appareil, puis scannez le code QR affiché pour démarrer le processus de liaison.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">Reliez votre appareil</string>
|
||||
<string name="fragment_enter_session_id_explanation">Rendez-vous dans Paramètres > Appareils reliés > Relier un appareil sur votre autre appareil, puis saisissez votre Session ID pour démarrer le processus de liaison.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">Saisissez votre Session ID</string>
|
||||
|
||||
<string name="activity_display_name_title_2">Choisissez votre nom d\'utilisateur</string>
|
||||
<string name="activity_display_name_explanation">Ce sera votre nom lorsque vous utiliserez Session. Il peut s\'agir de votre vrai nom, d\'un pseudo ou de ce que vous voulez.</string>
|
||||
<string name="activity_display_name_edit_text_hint">Saisissez un nom d\'utilisateur</string>
|
||||
<string name="activity_display_name_display_name_missing_error">Veuillez choisir un nom d\'utilisateur</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">Veuillez choisir un nom d\'utilisateur composé uniquement de caractères a-z, A-Z, 0-9 et _</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Veuillez choisir un nom d\'utilisateur plus court</string>
|
||||
|
||||
<string name="activity_pn_mode_recommended_option_tag">Recommandé</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Veuillez choisir une option</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">Vous n\'avez pas encore de contacts</string>
|
||||
<string name="activity_home_empty_state_button_title">Démarrez une Session</string>
|
||||
<string name="activity_home_leave_group_dialog_message">Voulez-vous vraiment quitter ce groupe ?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">Impossible de quitter le groupe</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">Voulez-vous vraiment supprimer cette conversation ?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Conversation supprimée</string>
|
||||
|
||||
<string name="activity_seed_title">Votre phrase de récupération</string>
|
||||
<string name="activity_seed_title_2">Voici votre phrase de récupération</string>
|
||||
<string name="activity_seed_explanation">Votre phrase de récupération est la clé principale de votre Session ID - vous pouvez l\'utiliser pour restaurer votre Session ID si vous perdez l\'accès à votre appareil. Conservez la dans un endroit sûr et ne la donnez à personne.</string>
|
||||
<string name="activity_seed_reveal_button_title">Appuyer pour révéler</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">Sécurisez votre compte en sauvegardant votre phrase de récupération</string>
|
||||
<string name="view_seed_reminder_subtitle_2">Appuyez et maintenez les mots masqués pour révéler votre phrase de récupération, puis stockez-la en toute sécurité pour sécuriser votre Session ID.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">Assurez-vous de conserver votre phrase de récupération dans un endroit sûr</string>
|
||||
|
||||
<string name="activity_path_title">Chemin</string>
|
||||
<string name="activity_path_explanation">Session occulte votre adresse IP en envoyant vos messages via plusieurs nœuds de service dans le réseau décentralisé de Session. Voici les pays par le biais desquels votre connexion est actuellement envoyée :</string>
|
||||
<string name="activity_path_device_row_title">Vous</string>
|
||||
<string name="activity_path_guard_node_row_title">Noeud d’entrée</string>
|
||||
<string name="activity_path_service_node_row_title">Noeud de service</string>
|
||||
<string name="activity_path_destination_row_title">Destination</string>
|
||||
<string name="activity_path_learn_more_button_title">En savoir plus</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">Nouvelle Session</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">Saisir un Session ID</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">Scanner un Code QR</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">Scannez le code QR d\'un utilisateur pour démarrer une session. Les codes QR peuvent se trouver en appuyant sur l\'icône du code QR dans les paramètres du compte.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">Saisissez le Session ID du destinataire</string>
|
||||
<string name="fragment_enter_public_key_explanation">Les utilisateurs peuvent partager leur Session ID depuis les paramètres du compte ou en utilisant le code QR.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">Session a besoin d\'accéder à l\'appareil photo pour scanner les codes QR</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Autoriser l\'accès</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">Nouveau groupe privé</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">Saisissez un nom de groupe</string>
|
||||
<string name="activity_create_closed_group_explanation">Les groupes privés prennent en charge jusqu\'à 10 membres et offrent le même niveau de confidentialité que les sessions individuelles.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">Vous n\'avez pas encore de contacts</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">Démarrer une session</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">Veuillez saisir un nom de groupe</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">Veuillez saisir un nom de groupe plus court</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">Veuillez sélectionner au moins 2 membres</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">Un groupe privé ne peut pas avoir plus de 10 membres</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">Un des membres de votre groupe a un Session ID non valide</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">Joindre un groupe public</string>
|
||||
<string name="activity_join_public_chat_error">Impossible de rejoindre le groupe</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">URL du groupe public</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">Scannez le code QR</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Scannez le code QR du groupe public que vous souhaitez rejoindre</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Saisissez une URL de groupe public</string>
|
||||
|
||||
<string name="activity_settings_title">Paramètres</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Saisissez un nom d\'utilisateur</string>
|
||||
<string name="activity_settings_display_name_missing_error">Veuillez choisir un nom d\'utilisateur</string>
|
||||
<string name="activity_settings_invalid_display_name_error">Veuillez choisir un nom d\'utilisateur composé uniquement de caractères a-z, A-Z, 0-9 et _</string>
|
||||
<string name="activity_settings_display_name_too_long_error">Veuillez choisir un nom d\'utilisateur plus court</string>
|
||||
<string name="activity_settings_privacy_button_title">Confidientalité </string>
|
||||
<string name="activity_settings_notifications_button_title">Notifications</string>
|
||||
<string name="activity_settings_chats_button_title">Chats</string>
|
||||
<string name="activity_settings_devices_button_title">Appareils reliés</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">Phrase de récupération</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">Effacer les données</string>
|
||||
|
||||
<string name="activity_notification_settings_title">Notifications</string>
|
||||
<string name="activity_notification_settings_style_section_title">Style de notification</string>
|
||||
<string name="activity_notification_settings_content_section_title">Contenu de notification</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">Confidientalité</string>
|
||||
|
||||
<string name="activity_chat_settings_title">Chats</string>
|
||||
|
||||
<string name="activity_linked_devices_title">Appareils reliés</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">Limite d\'appareils atteinte</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">Il n\'est actuellement pas possible de relier plus d\'un appareil.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">Impossible de déconnecter l\'appareil.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">Votre appareil a été déconnecté avec succès</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">Impossible de relier l\'appareil.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">Vous n\'avez encore relié aucun appareil</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">Relier un appareil</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Stratégie de notification</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">En attente d\'autorisation</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Liaison de l\'appareil autorisée</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">Veuillez vérifier que les mots ci-dessous correspondent à ceux affichés sur votre autre appareil.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">Votre appareil a été connecté avec succès</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">En attente d\'une demande de liaison</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">Demande de liaison reçue</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">Autorisation de la liaison de l\'appareil</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">Téléchargez Session sur votre autre appareil, puis cliquez sur \"Relier à un compte existant\" en bas de l\'écran de d’accueil. Si vous possédez déjà un compte sur votre autre appareil, vous devrez d\'abord le supprimer.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">Veuillez vérifier que les mots ci-dessous correspondent à ceux affichés sur votre autre appareil.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">Veuillez patienter pendant la création de la liaison. Cela peut prendre jusqu\'à une minute.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">Autoriser</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">Modifier le nom</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">Déconnecter l\'appareil</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">Saisissez un nom</string>
|
||||
|
||||
<string name="dialog_seed_title">Votre phrase de récupération</string>
|
||||
<string name="dialog_seed_explanation">Ceci est votre phrase de récupération. Elle vous permet de restaurer ou migrer votre Session ID vers un nouvel appareil.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">Effacer toutes les données</string>
|
||||
<string name="dialog_clear_all_data_explanation">Cela supprimera définitivement vos messages, vos sessions et vos contacts.</string>
|
||||
|
||||
<string name="activity_qr_code_title">Code QR</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">Afficher mon code QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">Scanner le code QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">Scannez le code QR d\'un autre utilisateur pour démarrer une session</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">Ceci est votre code QR. Les autres utilisateurs peuvent le scanner pour démarrer une session avec vous.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Partager le code QR</string>
|
||||
|
||||
<string name="session_reset_banner_message">Voulez-vous restaurer votre session avec %s ?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Fermer</string>
|
||||
<string name="session_reset_banner_restore_button_title">Restaurer</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">Contacts</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Groupes privés</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Groupes publics</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1296,5 +1296,185 @@ Ricevuto un messaggio di scambio chiavi per una versione di protocollo non valid
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">TAPPA PER SBLOCCARE</string>
|
||||
<string name="RegistrationLockDialog_reminder">Promemoria:</string>
|
||||
<string name="recipient_preferences__about">A riguardo</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<string name="continue_2">Continua</string>
|
||||
<string name="copy">Copia</string>
|
||||
<string name="invalid_url">URL non valido</string>
|
||||
<string name="copied_to_clipboard">Copiato negli appunti</string>
|
||||
<string name="device_linking_failed">Impossibile collegare il dispositivo.</string>
|
||||
<string name="next">Successivo</string>
|
||||
<string name="share">Condividi</string>
|
||||
<string name="invalid_session_id">Sessione ID non valido</string>
|
||||
<string name="cancel">Annulla</string>
|
||||
<string name="your_session_id">La tua Sessione ID</string>
|
||||
|
||||
<string name="activity_landing_title_2">La tua Sessione inizia qui...</string>
|
||||
<string name="activity_landing_register_button_title">Crea Sessione ID</string>
|
||||
<string name="activity_landing_restore_button_title">Continua la Sessione</string>
|
||||
<string name="activity_landing_link_button_title">Collegamento a un account esistente</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">Il dispositivo è stato scollegato correttamente</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">Che cos\'è una Sessione?</string>
|
||||
<string name="view_fake_chat_bubble_2">È un\'app di messaggistica decentralizzato e crittografato</string>
|
||||
<string name="view_fake_chat_bubble_3">Quindi non raccoglie informazioni personali o metadati di conversazione? Come funziona?</string>
|
||||
<string name="view_fake_chat_bubble_4">Utilizza una combinazione di routing anonimo avanzato e tecnologie di crittografia end-to-end.</string>
|
||||
<string name="view_fake_chat_bubble_5">Gli amici non lasciano i suoi amici di utilizzare messaggistica compromessa. Prego.</string>
|
||||
|
||||
<string name="activity_register_title">Ecco la tua Sessione ID</string>
|
||||
<string name="activity_register_explanation">La Sessione ID è l\'indirizzo univoco che le persone possono utilizzare per contattarti su una Sessione. Senza alcuna connessione con la tua vera identità, la Sessione ID è totalmente anonimo e privato fin dal incezione.</string>
|
||||
<string name="activity_register_public_key_copied_message">Copiato negli appunti</string>
|
||||
|
||||
<string name="activity_restore_title">Ripristina il tuo account</string>
|
||||
<string name="activity_restore_explanation">Inserisci la frase di recupero che ti è stata data quando ti sei registrato per ripristinare il tuo account.</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">Inserisci la frase di recupero</string>
|
||||
|
||||
<string name="activity_link_device_title">Collega dispositivo</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">Inserisci la Sessione ID</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">Scansiona il codice QR</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">Vai su Impostazioni> Dispositivi> Collega un dispositivo su un altro dispositivo, quindi effettua la scansione del codice QR visualizzato per avviare il processo di collegamento.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">Collega il dispositivo</string>
|
||||
<string name="fragment_enter_session_id_explanation">Vai su Impostazioni> Dispositivi> Collega un dispositivo su un altro dispositivo e inserisci la Sessione ID per avviare il processo di collegamento.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">Inserisci la Sessione ID</string>
|
||||
|
||||
<string name="activity_display_name_title_2">Scegli il nome da visualizzare</string>
|
||||
<string name="activity_display_name_explanation">Questo sarà il tuo nome quando usi una Sessione. Può essere il tuo vero nome, un soprannome o qualsiasi altra cosa.</string>
|
||||
<string name="activity_display_name_edit_text_hint">Inserisci il nome da visualizzare</string>
|
||||
<string name="activity_display_name_display_name_missing_error">Scegli il nome da visualizzare</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">Il nome visualizzare può contenere solo i caratteri a-z, AZ, 0-9 e _ </string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Scegli un nome più breve</string>
|
||||
|
||||
<string name="activity_pn_mode_recommended_option_tag">Consigliato</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Scegli un\'opzione</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">Non hai ancora nessun contatto</string>
|
||||
<string name="activity_home_empty_state_button_title">Inizia una sessione</string>
|
||||
<string name="activity_home_leave_group_dialog_message">Sei sicuro di voler lasciare questo gruppo?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">Impossibile lasciare il gruppo</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">Sei sicuro di voler eliminare questa conversazione?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Conversazione eliminata</string>
|
||||
|
||||
<string name="activity_seed_title">Frase di recupero</string>
|
||||
<string name="activity_seed_title_2">La frase di recupero</string>
|
||||
<string name="activity_seed_explanation">La frase di recupero è la chiave principale per la Sessione ID: puoi usarla per ripristinare la Sessione ID se perdi l\'accesso al dispositivo. Conserva la frase di recupero in un luogo sicuro e non rivelarla a nessuno.</string>
|
||||
<string name="activity_seed_reveal_button_title">Tieni premuto per rivelare</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">Proteggi il tuo account salvando la frase di recupero</string>
|
||||
<string name="view_seed_reminder_subtitle_2">Tocca e tieni premute le parole redatte per rivelare la frase di recupero, salva in modo sicuro per proteggere la tua Sessione ID.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">Assicurati di salvare la frase di recupero in un luogo sicuro</string>
|
||||
|
||||
<string name="activity_path_title">Percorso</string>
|
||||
<string name="activity_path_explanation">La Sessione nasconde il tuo IP facendo rimbalzare i messaggi attraverso diversi nodi di servizio nella sua rete decentralizzata. Questi sono i paesi in cui la connessione viene rimbalzata attualmente:</string>
|
||||
<string name="activity_path_device_row_title">Tu</string>
|
||||
<string name="activity_path_guard_node_row_title">Nodo di entrata</string>
|
||||
<string name="activity_path_service_node_row_title">Nodo di servizio</string>
|
||||
<string name="activity_path_destination_row_title">Destinazione</string>
|
||||
<string name="activity_path_learn_more_button_title">Per saperne di più</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">Nuova sessione</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">Inserisci la Sessione ID</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">Scansiona il codice QR</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">Scansiona il codice QR di un utente per avviare una sessione. Puoi trovare i codici QR toccando l\'icona Codice QR nelle impostazioni dell\'account.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">Inserisci la Sessione ID del destinatario</string>
|
||||
<string name="fragment_enter_public_key_explanation">Gli utenti possono condividere la propria Sessione ID accedendo alle impostazioni del proprio account e toccando Condividi la Sessione ID o condividendo il proprio codice QR.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">La Sessione richiede l\'accesso alla fotocamera per scansionare i codici QR</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Concedi l\'accesso alla fotocamera</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">Nuovo gruppo chiuso</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">Inserisci un nome per il gruppo</string>
|
||||
<string name="activity_create_closed_group_explanation">I gruppi chiusi supportano fino a 10 membri e forniscono le stesse protezioni per la privacy delle sessioni one-to-one.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">Non hai ancora nessun contatto</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">Inizia una sessione</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">Inserisci un nome per il gruppo</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">Inserisci un nome gruppo più breve</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">Scegli almeno 2 membri del gruppo</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">Un gruppo chiuso non può avere più di 10 membri</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">Uno dei membri del tuo gruppo ha una Sessione ID non valido</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">Unisciti a un gruppo aperto</string>
|
||||
<string name="activity_join_public_chat_error">Impossibile unirsi al gruppo</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">Apri l\'URL del gruppo</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">Scansiona il codice QR</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Scansiona il codice QR del gruppo aperto a cui desideri partecipare</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Inserisci l\'URL di un gruppo aperto</string>
|
||||
|
||||
<string name="activity_settings_title">Impostazioni</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Inserisci il nome da visualizzare</string>
|
||||
<string name="activity_settings_display_name_missing_error">Scegli il nome da visualizzare</string>
|
||||
<string name="activity_settings_invalid_display_name_error">Il nome visualizzare può contenere solo i caratteri a-z, AZ, 0-9 e _ </string>
|
||||
<string name="activity_settings_display_name_too_long_error">Scegli un nome più breve</string>
|
||||
<string name="activity_settings_privacy_button_title">Privacy</string>
|
||||
<string name="activity_settings_notifications_button_title">Notifiche</string>
|
||||
<string name="activity_settings_chats_button_title">Chat</string>
|
||||
<string name="activity_settings_devices_button_title">Dispositivi</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">Frase di recupero</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">Elimina dati</string>
|
||||
|
||||
<string name="activity_notification_settings_title">Notifiche</string>
|
||||
<string name="activity_notification_settings_style_section_title">Stile della notifica</string>
|
||||
<string name="activity_notification_settings_content_section_title">Contenuto della notifica</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">Privacy</string>
|
||||
|
||||
<string name="activity_chat_settings_title">Chat</string>
|
||||
|
||||
<string name="activity_linked_devices_title">Dispositivi</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">Limite del dispositivo raggiunto</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">Al momento non è consentito collegare più di un dispositivo.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">Impossibile scollegare il dispositivo.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">Il dispositivo è stato scollegato correttamente</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">Impossibile collegare il dispositivo.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">Non hai ancora collegato nessun dispositivo</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">Collega un dispositivo</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Strategia di notifica</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">In attesa di autorizzazione</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Collegamento al dispositivo autorizzato</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">Verifica che le seguenti parole corrispondano a quelle visualizzate sull\'altro dispositivo.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">Il dispositivo è stato collegato correttamente</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">In attesa del dispositivo</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">Richiesta di collegamento ricevuta</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">Autorizzazione al collegamento del dispositivo</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">Scarica la Sessione sull\'altro dispositivo e tocca Collega a un account esistente nella parte inferiore della schermata di destinazione. Se disponi già di un account sull\'altro dispositivo, dovrai prima eliminarlo.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">Verifica che le seguenti parole corrispondano a quelle visualizzate sull\'altro dispositivo.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">Attendi mentre viene creato il collegamento del dispositivo. Ciò può richiedere fino a un minuto.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">Autorizza</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">Cambia nome</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">Scollega dispositivo</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">Inserisci un nome</string>
|
||||
|
||||
<string name="dialog_seed_title">Frase di recupero</string>
|
||||
<string name="dialog_seed_explanation">Questa è la tua frase di recupero. Usala per ripristinare o migrare la Sessione ID a un nuovo dispositivo.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">Elimina tutti i dati</string>
|
||||
<string name="dialog_clear_all_data_explanation">Ciò eliminerà permanentemente i tuoi messaggi, sessioni e contatti.</string>
|
||||
|
||||
<string name="activity_qr_code_title">Codice QR</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">Visualizza il mio codice QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">Scansiona il codice QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">Scansiona il codice QR di un utente per iniziare una conversazione con questa persona</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">Questo è il tuo codice QR. Altri utenti possono scansionarlo per iniziare una sessione con te.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Condividi codice QR</string>
|
||||
|
||||
<string name="session_reset_banner_message">Desideri ripristinare la sessione con %s?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Rimuovi</string>
|
||||
<string name="session_reset_banner_restore_button_title">Ripristina</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">Contatti</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Gruppi chiusi</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Gruppi aperti</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1351,5 +1351,217 @@ Otrzymano wiadomość wymiany klucz dla niepoprawnej wersji protokołu.</string>
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">DOTKNIJ, ABY ODBLOKOWAĆ</string>
|
||||
<string name="RegistrationLockDialog_reminder">Przypomnienie:</string>
|
||||
<string name="recipient_preferences__about">O mnie</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<!--
|
||||
<string name="continue_2">Kontyntynuj</string>
|
||||
<string name="copy">Kopiuj</string>
|
||||
<string name="invalid_url">nieprawidłowy URL</string>
|
||||
<string name="copied_to_clipboard">Skopiowane do schowka</string>
|
||||
<string name="device_linking_failed">Nie można połączyć urządzenia.</string>
|
||||
<string name="next">Kolejny</string>
|
||||
<string name="share">Dzielić</string>
|
||||
<string name="invalid_session_id">Nieprawidłowy identyfikator Session</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="your_session_id">Twój identyfikator Session</string>
|
||||
|
||||
<string name="activity_landing_title_2">Twoja Session zaczyna się tutaj...</string>
|
||||
<string name="activity_landing_register_button_title">Utwórz identyfikator Session</string>
|
||||
<string name="activity_landing_restore_button_title">Kontynuuj swoją sesję</string>
|
||||
<string name="activity_landing_link_button_title">Połącz z istniejącym kontem</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">Twoje urządzenie zostało rozłączone pomyślnie</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">Jaka jest Session</string>
|
||||
<string name="view_fake_chat_bubble_2">To zdecentralizowana, szyfrowana aplikacja do przesyłania wiadomości</string>
|
||||
<string name="view_fake_chat_bubble_3">Więc nie zbiera moich danych osobowych ani metadanych z mojej rozmowy? Jak to działa?</string>
|
||||
<string name="view_fake_chat_bubble_4">Wykorzystując połączenie zaawansowanych anonimowych tras i technologii szyfrowania end-to-end.</string>
|
||||
<string name="view_fake_chat_bubble_5">Znajomi nie pozwalają znajomym korzystać z zainfekowanych komunikatorów. Nie ma za co.</string>
|
||||
|
||||
<string name="activity_register_title">Przywitaj się z identyfikatorem Session</string>
|
||||
<string name="activity_register_explanation">Twój identyfikator Session to unikalny adres, za pomocą którego można się z Tobą kontaktować w Sesji. Bez połączenia z twoją prawdziwą tożsamością, identyfikator Session jest z założenia całkowicie anonimowy i prywatny.</string>
|
||||
<string name="activity_register_public_key_copied_message">Skopiowane do schowka</string>
|
||||
|
||||
<string name="activity_restore_title">Przywróć swoje konto</string>
|
||||
<string name="activity_restore_explanation">Wprowadź frazę odzyskiwania, która została Ci przekazana podczas rejestracji w celu przywrócenia konta.</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">Wpisz swoją frazę odzyskiwania</string>
|
||||
|
||||
<string name="activity_link_device_title">Połącz urządzenie</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">Wpisz identyfikator Session</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">Skanowania QR code</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">Przejdź do Ustawienia > Urządzenia > Połącz urządzenie na drugim urządzeniu, a następnie zeskanuj kod QR, który się pojawi, aby rozpocząć proces łączenia.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">Połącz swoje urządzenie</string>
|
||||
<string name="fragment_enter_session_id_explanation">Przejdź do Ustawienia > Urządzenia > Połącz urządzenie na drugim urządzeniu, a następnie wprowadź tutaj identyfikator Session, aby rozpocząć proces łączenia.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">Wpisz swój identyfikator Session</string>
|
||||
|
||||
<string name="activity_display_name_title_2">Wybierz swoją nazwę wyświetlaną</string>
|
||||
<string name="activity_display_name_explanation">To będzie twoje imię, kiedy będziesz używać Sesji. Może to być twoje prawdziwe imię, alias lub cokolwiek innego, co lubisz.</string>
|
||||
<string name="activity_display_name_edit_text_hint">Wprowadź wyświetlaną nazwe</string>
|
||||
<string name="activity_display_name_display_name_missing_error">Wybierz wyświetlaną nazwę</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">Wybierz wyświetlaną nazwę, która składa się tylko z znaków az, AZ, 0–9 i _</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Wybierz krótszą nazwę wyświetlaną</string>
|
||||
|
||||
<string name="activity_pn_mode_title">Powiadomienia push</string>
|
||||
<string name="activity_pn_mode_explanation">Istnieją dwa sposoby obsługi powiadomień wypychanych przez sesję. Przeczytaj uważnie opisy, zanim wybierzesz.</string>
|
||||
<string name="activity_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||
<string name="activity_pn_mode_fcm_option_explanation">Sesja będzie korzystała z usługi Firebase Cloud Messaging do otrzymywania powiadomień push. Będziesz powiadamiany o nowych wiadomościach niezawodnie i natychmiast. Korzystanie z FCM oznacza, że Twój adres IP i token urządzenia zostaną udostępnione Google. Jeśli używasz powiadomień push dla innych aplikacji, tak już będzie. Twój adres IP i token urządzenia będą również narażone na działanie Lokiego, ale wiadomości będą nadal kierowane do cebuli i szyfrowane od końca do końca, więc zawartość wiadomości pozostanie całkowicie prywatna.</string>
|
||||
<string name="activity_pn_mode_background_polling_option_title">Sondowanie w tle</string>
|
||||
<string name="activity_pn_mode_background_polling_option_explanation">Sesja od czasu do czasu sprawdza nowe wiadomości w tle. Gwarantuje to pełną ochronę metadanych, ale powiadomienia mogą być znacznie opóźnione.</string>
|
||||
<string name="activity_pn_mode_recommended_option_tag">Zalecana</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Wybierz opcję</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">Nie masz jeszcze żadnych kontaktów</string>
|
||||
<string name="activity_home_empty_state_button_title">Rozpocznij sesję</string>
|
||||
<string name="activity_home_leave_group_dialog_message">Czy na pewno chcesz opuścić tę grupę?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">Nie można opuścić grupy</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">Czy na pewno chcesz usunąć tę rozmowę?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Rozmowa usunięta</string>
|
||||
|
||||
<string name="sheet_pn_mode_title">Powiadomienia push</string>
|
||||
<string name="sheet_pn_mode_explanation">Sesja oferuje teraz dwa sposoby obsługi powiadomień wypychanych. Przeczytaj uważnie opisy, zanim wybierzesz.</string>
|
||||
<string name="sheet_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||
<string name="sheet_pn_mode_fcm_option_explanation">Sesja będzie korzystała z usługi Firebase Cloud Messaging do otrzymywania powiadomień push. Będziesz powiadamiany o nowych wiadomościach niezawodnie i natychmiast. Korzystanie z FCM oznacza, że Twój adres IP i token urządzenia zostaną udostępnione Google. Jeśli używasz powiadomień push dla innych aplikacji, tak już będzie. Twój adres IP i token urządzenia będą również narażone na działanie Lokiego, ale wiadomości będą nadal kierowane do cebuli i szyfrowane od końca do końca, więc zawartość wiadomości pozostanie całkowicie prywatna.</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_title">Sondowanie w tle</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_explanation">Sesja od czasu do czasu sprawdza nowe wiadomości w tle. Gwarantuje to pełną ochronę metadanych, ale powiadomienia mogą być znacznie opóźnione.</string>
|
||||
<string name="sheet_pn_mode_recommended_option_tag">Zalecana</string>
|
||||
<string name="sheet_pn_mode_no_option_picked_dialog_title">Wybierz opcję</string>
|
||||
<string name="sheet_pn_mode_confirm_button_title">Potwierdzać</string>
|
||||
<string name="sheet_pn_mode_skip_button_title">Pominąć</string>
|
||||
|
||||
<string name="activity_seed_title">Twoja fraza odzyskiwania</string>
|
||||
<string name="activity_seed_title_2">Poznaj swoją frazę odzyskiwania</string>
|
||||
<string name="activity_seed_explanation">Twoja fraza odzyskiwania jest kluczem głównym do identyfikatora Session - możesz go użyć do przywrócenia identyfikatora Session, jeśli stracisz dostęp do urządzenia. Przechowuj swoją frazę odzyskiwania w bezpiecznym miejscu i nikomu jej nie udostępniaj.</string>
|
||||
<string name="activity_seed_reveal_button_title">Przytrzymaj, aby odsłonić</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">Zabezpiecz swoje konto, zapisując frazę odzyskiwania</string>
|
||||
<string name="view_seed_reminder_subtitle_2">Stuknij i przytrzymaj zredagowane słowa, aby odsłonić frazę odzyskiwania, a następnie przechowuj ją bezpiecznie, aby zabezpieczyć identyfikator Session.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">Pamiętaj, aby przechowywać frazę odzyskiwania w bezpiecznym miejscu</string>
|
||||
|
||||
<string name="activity_path_title">Ścieżka</string>
|
||||
<string name="activity_path_explanation">Sesja ukrywa Twój adres IP, odbijając wiadomości przez kilka węzłów usług w zdecentralizowanej sieci Session. Oto kraje, w których obecnie Twoje połączenie jest odbijane:</string>
|
||||
<string name="activity_path_device_row_title">ty</string>
|
||||
<string name="activity_path_guard_node_row_title">Węzeł wejścia</string>
|
||||
<string name="activity_path_service_node_row_title">Węzeł serwisowy</string>
|
||||
<string name="activity_path_destination_row_title">Miejsce docelowe</string>
|
||||
<string name="activity_path_learn_more_button_title">Ucz się więcej</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">Nowa Session</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">Wpisz identyfikator Session</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">Skanowania QR code</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">Zeskanuj kod QR użytkownika, aby rozpocząć sesję. Kody QR można znaleźć, dotykając ikony kodu QR w ustawieniach konta.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">Wprowadź identyfikator Session odbiorcy</string>
|
||||
<string name="fragment_enter_public_key_explanation">Użytkownicy mogą udostępnić swój identyfikator Session, przechodząc do ustawień konta i stukając opcję Udostępnij identyfikator Session lub udostępniając kod QR.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">Sesja wymaga dostępu do kamery, aby skanować kody QR</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Udziel dostępu do kamery</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">Nowa grupa zamknięta</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">Wpisz nazwę grupy</string>
|
||||
<string name="activity_create_closed_group_explanation">Grupy zamknięte obsługują do 10 członków i zapewniają taką samą ochronę prywatności jak sesje jeden na jednego.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">Nie masz jeszcze żadnych kontaktów</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">Rozpocznij sesję</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">Wpisz nazwę grupy</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">Wprowadź krótszą nazwę grupy</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">Wybierz przynajmniej 2 członków grupy</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">Grupa zamknięta nie może mieć więcej niż 10 członków</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">Jeden z członków Twojej grupy ma nieprawidłowy identyfikator Session</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">Dołącz do Open Group</string>
|
||||
<string name="activity_join_public_chat_error">Nie można dołączyć do grupy</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">Otwórz adres URL grupy</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">Skanowania QR code</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Zeskanuj kod QR otwartej grupy, do której chcesz dołączyć</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Wprowadź adres URL otwartej grupy</string>
|
||||
<string name="fragment_enter_chat_url_privacy_warning">Do otwartych grup może dołączyć każdy i nie zapewniają pełnej ochrony prywatności</string>
|
||||
|
||||
<string name="activity_settings_title">Ustawienia</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Wprowadź wyświetlaną nazwe</string>
|
||||
<string name="activity_settings_display_name_missing_error">Wybierz wyświetlaną nazwę</string>
|
||||
<string name="activity_settings_invalid_display_name_error">Wybierz wyświetlaną nazwę, która składa się tylko z znaków az, AZ, 0–9 i _</string>
|
||||
<string name="activity_settings_display_name_too_long_error">Wybierz krótszą nazwę wyświetlaną</string>
|
||||
<string name="activity_settings_privacy_button_title">Prywatność</string>
|
||||
<string name="activity_settings_notifications_button_title">Powiadomienia</string>
|
||||
<string name="activity_settings_chats_button_title">Czaty</string>
|
||||
<string name="activity_settings_devices_button_title">Urządzenia</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">Zwrot odzyskiwania</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">Wyczyść dane</string>
|
||||
|
||||
<string name="activity_notification_settings_title">Powiadomienia</string>
|
||||
<string name="activity_notification_settings_style_section_title">Styl powiadomienia</string>
|
||||
<string name="activity_notification_settings_content_section_title">Treść powiadomienia</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">Prywatność</string>
|
||||
|
||||
<string name="activity_chat_settings_title">Czaty</string>
|
||||
|
||||
<string name="activity_linked_devices_title">Urządzenia</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">Osiągnięto limit urządzeń</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">Obecnie nie można łączyć więcej niż jednego urządzenia.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">Nie można odłączyć urządzenia.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">Twoje urządzenie zostało rozłączone pomyślnie</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">Nie można połączyć urządzenia.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">Nie podłączyłeś jeszcze żadnych urządzeń</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">Połącz urządzenie (Beta)</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Strategia powiadomień</string>
|
||||
<string name="preferences_notifications_use_fcm_option_title">Użyj FCM</string>
|
||||
<string name="preferences_notifications_use_fcm_option_explanation">Korzystanie z Firebase Cloud Messaging pozwala na bardziej niezawodne powiadomienia wypychane, ale udostępnia adres IP i token urządzenia Google i Lokiemu.</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">Oczekiwanie na autoryzację</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Link do urządzenia autoryzowany</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">Sprawdź, czy poniższe słowa odpowiadają słowom wyświetlanym na drugim urządzeniu.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">Twoje urządzenie zostało pomyślnie połączone</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">Oczekiwanie na urządzenie</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">Otrzymano żądanie połączenia</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">Link do urządzenia autoryzującego</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">Pobierz Sesję na drugie urządzenie i dotknij Połącz z istniejącym kontem u dołu ekranu docelowego. Jeśli masz już konto na drugim urządzeniu, najpierw musisz je usunąć.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">Sprawdź, czy poniższe słowa odpowiadają słowom wyświetlanym na drugim urządzeniu.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">Poczekaj, aż łącze urządzenia zostanie utworzone. Może to potrwać do minuty.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">Autoryzować</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">Zmień nazwę</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">Odłącz urządzenie</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">Wpisz imię</string>
|
||||
|
||||
<string name="dialog_seed_title">Twoja fraza odzyskiwania</string>
|
||||
<string name="dialog_seed_explanation">To jest twoja fraza odzyskiwania. Dzięki niemu możesz przywrócić lub przenieść identyfikator Session na nowe urządzenie.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">Wyczyść wszystkie dane</string>
|
||||
<string name="dialog_clear_all_data_explanation">Spowoduje to trwałe usunięcie wiadomości, Session i kontaktów.</string>
|
||||
|
||||
<string name="activity_qr_code_title">Kod QR</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">Wyświetl mój kod QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">Skanowania QR code</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">Zeskanuj czyjś kod QR, aby rozpocząć z nim rozmowę</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">To jest twój kod QR. Inni użytkownicy mogą go zeskanować, aby rozpocząć z tobą sesję.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Udostępnij kod QR</string>
|
||||
|
||||
<string name="view_friend_request_accept_button_title">Zaakceptować</string>
|
||||
<string name="view_friend_request_reject_button_title">Upadek</string>
|
||||
<string name="view_friend_request_incoming_pending_message">%1$s wysłał Ci prośbę o sesję</string>
|
||||
<string name="view_friend_request_incoming_accepted_message">Zaakceptowałeś prośbę o sesję %1$s</string>
|
||||
<string name="view_friend_request_incoming_declined_message">Odrzuciłeś żądanie Session %1$s</string>
|
||||
<string name="view_friend_request_incoming_expired_message">Żądanie Session %1$s wygasło</string>
|
||||
<string name="view_friend_request_outgoing_pending_message">Wysłano %1$s żądanie Session</string>
|
||||
<string name="view_friend_request_outgoing_accepted_message">%1$s zaakceptował Twoje żądanie Session</string>
|
||||
<string name="view_friend_request_outgoing_expired_message">Twoje żądanie Session do %1$s wygasło</string>
|
||||
|
||||
<string name="session_reset_banner_message">Czy chcesz przywrócić sesję za pomocą %s?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Oddalić</string>
|
||||
<string name="session_reset_banner_restore_button_title">Przywracać</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">Łączność</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Grupy zamknięte</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Otwórz grupy</string>
|
||||
-->
|
||||
|
||||
</resources>
|
||||
|
@ -1299,5 +1299,185 @@
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">TOQUE PARA DESBLOQUEAR</string>
|
||||
<string name="RegistrationLockDialog_reminder">Lembrete:</string>
|
||||
<string name="recipient_preferences__about">Sobre</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<string name="continue_2">Continuar</string>
|
||||
<string name="copy">Copiar</string>
|
||||
<string name="invalid_url">URL inválido</string>
|
||||
<string name="copied_to_clipboard">Copiado para a área de transferência</string>
|
||||
<string name="device_linking_failed">Não foi possível sincronizar o dispositivo.</string>
|
||||
<string name="next">Próximo</string>
|
||||
<string name="share">Compartilhar</string>
|
||||
<string name="invalid_session_id">ID Session inválido</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="your_session_id">Seu ID Session</string>
|
||||
|
||||
<string name="activity_landing_title_2">O Session começa aqui...</string>
|
||||
<string name="activity_landing_register_button_title">Criar ID Session</string>
|
||||
<string name="activity_landing_restore_button_title">Continuar com seu Session</string>
|
||||
<string name="activity_landing_link_button_title">Link para uma conta existente</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">O seu dispositivo foi dessincronizado com sucesso</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">O que é o Session?</string>
|
||||
<string name="view_fake_chat_bubble_2">É um aplicativo de mensagens criptografado e descentralizado</string>
|
||||
<string name="view_fake_chat_bubble_3">Então ele não coleta minhas informações pessoais ou meus metadados de conversa? Como funciona?</string>
|
||||
<string name="view_fake_chat_bubble_4">Usando uma combinação de tecnologias avançadas de roteamento anônimo e criptografia de ponta a ponta.</string>
|
||||
<string name="view_fake_chat_bubble_5">Amigos não deixam amigos usarem aplicativos de mensagem comprometidos. De nada.</string>
|
||||
|
||||
<string name="activity_register_title">Diga olá ao seu ID Session</string>
|
||||
<string name="activity_register_explanation">Seu ID Session é o endereço exclusivo que as pessoas podem usar para entrar em contato com você no Session. Sem conexão com sua identidade real, seu ID Session é totalmente anônimo e privado por definição.</string>
|
||||
<string name="activity_register_public_key_copied_message">Copiado para a área de transferência</string>
|
||||
|
||||
<string name="activity_restore_title">Restaurar sua conta</string>
|
||||
<string name="activity_restore_explanation">Digite a frase de recuperação que lhe foi fornecida quando você se inscreveu para restaurar sua conta.</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">Digite sua frase de recuperação</string>
|
||||
|
||||
<string name="activity_link_device_title">Sincronize dispositivo</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">Digite o ID Session</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">Escanear código QR</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">Vá para Configurações > Dispositivos > Sincronize um dispositivo no seu outro dispositivo e, em seguida, escaneie o código QR que aparece para iniciar o processo de sincronização.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">Sincronize seu dispositivo</string>
|
||||
<string name="fragment_enter_session_id_explanation">Vá para Configurações > Dispositivos > Sincronizar um dispositivo no seu outro dispositivo e insira seu ID Session aqui para iniciar o processo de sincronização.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">Digite seu ID Session</string>
|
||||
|
||||
<string name="activity_display_name_title_2">Escolha seu nome de exibição</string>
|
||||
<string name="activity_display_name_explanation">Este será o seu nome quando usar o Session. Pode ser seu nome verdadeiro, um apelido ou qualquer outra coisa que você quiser.</string>
|
||||
<string name="activity_display_name_edit_text_hint">Digite um nome de exibição</string>
|
||||
<string name="activity_display_name_display_name_missing_error">Escolha um nome de exibição</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">Escolha um nome de exibição que contenha apenas caracteres az, AZ, 0-9 e _</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Escolha um nome de exibição mais curto</string>
|
||||
|
||||
<string name="activity_pn_mode_recommended_option_tag">Recomendado</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Escolha uma opção</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">Você ainda não possui contatos</string>
|
||||
<string name="activity_home_empty_state_button_title">Iniciar uma sessão</string>
|
||||
<string name="activity_home_leave_group_dialog_message">Tem certeza de que deseja sair deste grupo?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">Não foi possível sair do grupo</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">Tem certeza de que deseja excluir esta conversa?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Conversa excluída</string>
|
||||
|
||||
<string name="activity_seed_title">Sua frase de recuperação</string>
|
||||
<string name="activity_seed_title_2">Revele sua frase de recuperação</string>
|
||||
<string name="activity_seed_explanation">Sua frase de recuperação é a chave mestra do seu ID Session - você pode usá-la para restaurar seu ID Session se perder o acesso ao seu dispositivo. Armazene sua frase de recuperação em um local seguro e não a entregue a ninguém.</string>
|
||||
<string name="activity_seed_reveal_button_title">Segure para revelar</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">Proteja sua conta salvando sua frase de recuperação</string>
|
||||
<string name="view_seed_reminder_subtitle_2">Toque e segure as palavras editadas para revelar sua frase de recuperação e armazene-a com segurança para proteger seu ID Session.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">Guarde sua frase de recuperação em um local seguro</string>
|
||||
|
||||
<string name="activity_path_title">Caminho</string>
|
||||
<string name="activity_path_explanation">O Session oculta seu IP ao enviar suas mensagens através de vários Nós de Serviço na rede descentralizada do Session. Estes são os países pelos quais sua conexão está sendo ricocheteada no momento:</string>
|
||||
<string name="activity_path_device_row_title">Você</string>
|
||||
<string name="activity_path_guard_node_row_title">Nó de Entrada</string>
|
||||
<string name="activity_path_service_node_row_title">Nó de Serviço</string>
|
||||
<string name="activity_path_destination_row_title">Destino</string>
|
||||
<string name="activity_path_learn_more_button_title">Saber mais</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">Nova Sessão</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">Digite o ID Session</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">Escanear código QR</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">Escaneie o código QR de um usuário para iniciar uma sessão. Os códigos QR podem ser encontrados tocando no ícone de código QR nas configurações da conta.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">Digite o ID Session do destinatário</string>
|
||||
<string name="fragment_enter_public_key_explanation">Os usuários podem compartilhar seus IDs Session acessando as configurações da conta e tocando em Compartilhar ID Session, ou compartilhando o código QR.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">O Session precisa de acesso à câmera para escanear códigos QR</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Conceder acesso à câmera</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">Novo grupo fechado</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">Digite o nome do grupo</string>
|
||||
<string name="activity_create_closed_group_explanation">Grupos fechados suportam até 10 membros e fornecem as mesmas proteções de privacidade que as sessões individuais.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">Você ainda não possui contatos</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">Iniciar uma sessão</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">Digite um nome de grupo</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">Digite um nome de grupo mais curto</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">Escolha pelo menos 2 membros do grupo</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">Um grupo fechado não pode ter mais de 10 membros</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">Um dos membros do seu grupo tem um ID Session inválido</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">Participar em grupo aberto</string>
|
||||
<string name="activity_join_public_chat_error">Não foi possível entrar no grupo</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">URL do grupo aberto</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">Escanear código QR</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Escaneie o código QR do grupo aberto no qual você deseja entrar</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Digite a URL do grupo aberto</string>
|
||||
|
||||
<string name="activity_settings_title">Configurações</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Digite um nome de exibição</string>
|
||||
<string name="activity_settings_display_name_missing_error">Escolha um nome de exibição</string>
|
||||
<string name="activity_settings_invalid_display_name_error">Escolha um nome de exibição que contenha apenas caracteres az, AZ, 0-9 e _</string>
|
||||
<string name="activity_settings_display_name_too_long_error">Escolha um nome de exibição mais curto</string>
|
||||
<string name="activity_settings_privacy_button_title">Privacidade</string>
|
||||
<string name="activity_settings_notifications_button_title">Notificações</string>
|
||||
<string name="activity_settings_chats_button_title">Bate-papos</string>
|
||||
<string name="activity_settings_devices_button_title">Dispositivos</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">Frase de recuperação</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">Apagar os dados</string>
|
||||
|
||||
<string name="activity_notification_settings_title">Notificações</string>
|
||||
<string name="activity_notification_settings_style_section_title">Estilo de notificação</string>
|
||||
<string name="activity_notification_settings_content_section_title">Conteúdo da notificação</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">Privacidade</string>
|
||||
|
||||
<string name="activity_chat_settings_title">Bate-papos</string>
|
||||
|
||||
<string name="activity_linked_devices_title">Dispositivos</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">Limite de dispositivos atingido</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">No momento, não é permitido sincronizar mais de um dispositivo.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">Não foi possível dessincronizar o dispositivo.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">O seu dispositivo foi dessincronizado com sucesso</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">Não foi possível sincronizar o dispositivo.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">Você ainda não sincronizou nenhum dispositivo</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">Sincronizar um dispositivo</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Estratégia de notificação</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">Esperando autorização</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">sincronização de dispositivo autorizada</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">Verifique se as palavras abaixo correspondem às mostradas em seu outro dispositivo.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">Seu dispositivo foi sincronizado com sucesso</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">Aguardando dispositivo</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">Solicitação de sincronização recebida</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">Autorizando a sincronização de dispositivo</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">Baixe o Session em seu outro dispositivo e toque em Sincronizar a uma conta existente na parte inferior da tela de início. Se você já possui uma conta em seu outro dispositivo, precisará excluí-la primeiro.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">Verifique se as palavras abaixo correspondem às mostradas em seu outro dispositivo.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">Aguarde enquanto a sincronização do dispositivo é criada. Isso pode levar até um minuto.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">Autorizar</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">Mudar o nome</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">Dessincronizar dispositivo</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">Insira o nome</string>
|
||||
|
||||
<string name="dialog_seed_title">Sua frase de recuperação</string>
|
||||
<string name="dialog_seed_explanation">Esta é sua frase de recuperação. Com ela, você pode restaurar ou migrar seu ID Session para um novo dispositivo.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">Limpar todos os dados</string>
|
||||
<string name="dialog_clear_all_data_explanation">Isso excluirá permanentemente suas mensagens, sessões e contatos.</string>
|
||||
|
||||
<string name="activity_qr_code_title">Código QR</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">Ver meu código QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">Escanear código QR</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">Escaneie o código QR de alguém para iniciar uma conversa com essa pessoa</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">Este é o seu código QR. Outros usuários podem escaneá-lo para iniciar uma sessão com você.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Compartilhar código QR</string>
|
||||
|
||||
<string name="session_reset_banner_message">Deseja restaurar sua sessão com %s?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Dispensar</string>
|
||||
<string name="session_reset_banner_restore_button_title">Restaurar</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">Contatos</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Grupos fechados</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Grupos abertos</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1353,5 +1353,185 @@
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">КОСНИТЕСЬ, ЧТОБЫ РАЗБЛОКИРОВАТЬ</string>
|
||||
<string name="RegistrationLockDialog_reminder">Напоминание:</string>
|
||||
<string name="recipient_preferences__about">О Session</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<string name="continue_2">Продолжить</string>
|
||||
<string name="copy">Копировать</string>
|
||||
<string name="invalid_url">Неверная ссылка</string>
|
||||
<string name="copied_to_clipboard">Скопировано в буфер обмена</string>
|
||||
<string name="device_linking_failed">Не удалось привязать устройство.</string>
|
||||
<string name="next">Далее</string>
|
||||
<string name="share">Поделиться</string>
|
||||
<string name="invalid_session_id">Неверный Session ID</string>
|
||||
<string name="cancel">Отмена</string>
|
||||
<string name="your_session_id">Ваш Session ID</string>
|
||||
|
||||
<string name="activity_landing_title_2">Здесь начинается Session...</string>
|
||||
<string name="activity_landing_register_button_title">Создать Session ID</string>
|
||||
<string name="activity_landing_restore_button_title">Восстановить Session ID</string>
|
||||
<string name="activity_landing_link_button_title">Привязать к существующему аккаунту</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">Ваше устройство успешно отвязано</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">Что такое Session?</string>
|
||||
<string name="view_fake_chat_bubble_2">Это децентрализованное, зашифрованное приложение для обмена сообщениями</string>
|
||||
<string name="view_fake_chat_bubble_3">Значит ли это, что оно не собирает мою личную информацию или метаданные моего разговора? Как оно работает?</string>
|
||||
<string name="view_fake_chat_bubble_4">С использованием комбинации передовых технологий анонимной маршрутизации и сквозного шифрования.</string>
|
||||
<string name="view_fake_chat_bubble_5">Друзья не позволят друзьям использовать ненадежные мессенджеры. Пользуйтесь на здоровье.</string>
|
||||
|
||||
<string name="activity_register_title">Познакомьтесь со своим Session ID</string>
|
||||
<string name="activity_register_explanation">Ваш Session ID - это уникальный адрес, который могут использовать другие люди для связи с вами при помощи Session. Поскольку ваш Session ID никак не связан с вашей настоящей личностью, он по определению является полностью анонимным и конфиденциальным.</string>
|
||||
<string name="activity_register_public_key_copied_message">Скопировано в буфер обмена</string>
|
||||
|
||||
<string name="activity_restore_title">Восстановите свой аккаунт</string>
|
||||
<string name="activity_restore_explanation">Для восстановления учетной записи введите секретную фразу, которая была предоставлена вам при регистрации.</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">Введите секретную фразу</string>
|
||||
|
||||
<string name="activity_link_device_title">Привязать устройство</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">Введите Session ID</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">Сканировать QR-код</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">Перейдите в «Настройки» > «Устройства»> «Привязать устройство» на другом устройстве, а затем отсканируйте появившийся QR-код, чтобы начать процесс привязки.</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">Привяжите свое устройство</string>
|
||||
<string name="fragment_enter_session_id_explanation">Перейдите в «Настройки» > «Устройства» > «Привязать устройство» на другом устройстве и введите сюда свой Session ID, чтобы начать процесс привязки.</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">Введите свой Session ID</string>
|
||||
|
||||
<string name="activity_display_name_title_2">Выберите ваше отображаемое имя</string>
|
||||
<string name="activity_display_name_explanation">Это имя будет отображаться, когда вы используете Session. Это может быть ваше настоящее имя, псевдоним или что угодно по вашему выбору.</string>
|
||||
<string name="activity_display_name_edit_text_hint">Введите отображаемое имя</string>
|
||||
<string name="activity_display_name_display_name_missing_error">Пожалуйста, выберите отображаемое имя</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">Пожалуйста, выберите отображаемое имя состоящее только из символов a-z, A-Z, 0-9 и _</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Пожалуйста, выберите более короткое отображаемое имя</string>
|
||||
|
||||
<string name="activity_pn_mode_recommended_option_tag">Рекомендуется</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Пожалуйста, выберите метод</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">У вас еще нет контактов</string>
|
||||
<string name="activity_home_empty_state_button_title">Начать Сессию</string>
|
||||
<string name="activity_home_leave_group_dialog_message">Вы уверены, что хотите покинуть эту группу?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">Не удалось покинуть группу</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">Вы уверены, что хотите удалить этот разговор?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Разговор удален</string>
|
||||
|
||||
<string name="activity_seed_title">Ваша секретная фраза для восстановления</string>
|
||||
<string name="activity_seed_title_2">А вот и ваша секретная фраза для восстановления</string>
|
||||
<string name="activity_seed_explanation">Ваша секретная фраза является главным ключом к вашему Session ID. Вы можете использовать ее для восстановления Session ID, если потеряете доступ к своему устройству. Сохраните свою секретную фразу в безопасном месте, и никому её не передавайте.</string>
|
||||
<string name="activity_seed_reveal_button_title">Удерживайте, чтобы показать</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">Защитите свой аккаунт, сохранив секретную фразу</string>
|
||||
<string name="view_seed_reminder_subtitle_2">Нажмите и удерживайте сокращенные слова, чтобы открыть секретную фразу, а затем сохраните ее в надежном месте, чтобы защитить свой Session ID.</string>
|
||||
<string name="view_seed_reminder_subtitle_3">Обязательно сохраните секретную фразу в надежном месте.</string>
|
||||
|
||||
<string name="activity_path_title">Маршрут</string>
|
||||
<string name="activity_path_explanation">Session скрывает ваш IP, перенаправляя ваши сообщения через несколько сервисных узлов своей децентрализованной сети. Вот страны, через которые в данный момент проходит ваш сеанс связи:</string>
|
||||
<string name="activity_path_device_row_title">Вы</string>
|
||||
<string name="activity_path_guard_node_row_title">Узел входа</string>
|
||||
<string name="activity_path_service_node_row_title">Служебный узел</string>
|
||||
<string name="activity_path_destination_row_title">Место назначения</string>
|
||||
<string name="activity_path_learn_more_button_title">Узнать больше</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">Новый Диалог</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">Введите Session ID</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">Сканировать QR-код</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">Сканируйте QR-код пользователя, чтобы начать сессию. QR-коды можно найти, нажав значок QR-кода в настройках учетной записи.</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">Введите Session ID получателя</string>
|
||||
<string name="fragment_enter_public_key_explanation">Пользователи могут поделиться своим Session ID, зайдя в настройки своей учетной записи и нажав «Отправить Session ID», или поделившись своим QR-кодом.</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">Session нужен доступ к камере для сканирования QR-кодов</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">Предоставить доступ к камере</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">Новая закрытая группа</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">Введите название группы</string>
|
||||
<string name="activity_create_closed_group_explanation">Закрытые группы поддерживают до 10 участников и обеспечивают те же меры защиты конфиденциальности, что и сессии один-на-один.</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">У вас еще нет контактов</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">Начать Сессию</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">Пожалуйста, введите название группы</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">Пожалуйста, введите более короткое имя группы</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">Пожалуйста, выберите как минимум 2 участников группы</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">В закрытой группе не может быть больше 10 участников</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">Один из участников вашей группы имеет недопустимый Session ID</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">Присоединиться к открытой группе</string>
|
||||
<string name="activity_join_public_chat_error">Не удалось присоединиться к группе</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">URL открытой группы</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">Сканировать QR-код</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Отсканируйте QR-код открытой группы, в которую вы хотите вступить</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Введите URL открытой группы</string>
|
||||
|
||||
<string name="activity_settings_title">Настройки</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Введите отображаемое имя</string>
|
||||
<string name="activity_settings_display_name_missing_error">Пожалуйста, выберите отображаемое имя</string>
|
||||
<string name="activity_settings_invalid_display_name_error">Пожалуйста, выберите отображаемое имя состоящее только из символов a-z, A-Z, 0-9 и _</string>
|
||||
<string name="activity_settings_display_name_too_long_error">Пожалуйста, выберите более короткое отображаемое имя</string>
|
||||
<string name="activity_settings_privacy_button_title">Конфиденциальность</string>
|
||||
<string name="activity_settings_notifications_button_title">Уведомления</string>
|
||||
<string name="activity_settings_chats_button_title">Чаты</string>
|
||||
<string name="activity_settings_devices_button_title">Устройства</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">Секретная фраза</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">Очистить данные</string>
|
||||
|
||||
<string name="activity_notification_settings_title">Уведомления</string>
|
||||
<string name="activity_notification_settings_style_section_title">Стиль уведомлений</string>
|
||||
<string name="activity_notification_settings_content_section_title">Содержание уведомления</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">Конфиденциальность</string>
|
||||
|
||||
<string name="activity_chat_settings_title">Чаты</string>
|
||||
|
||||
<string name="activity_linked_devices_title">Устройства</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">Достигнуто предельное кол-во устройств</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">В настоящее время запрещено привязывать более одного устройства.</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">Не удалось отвязать устройство.</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">Ваше устройство успешно отвязано</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">Не удалось привязать устройство.</string>
|
||||
<string name="activity_linked_devices_empty_state_message">Вы еще не привязали ни одного устройства</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">Привязать устройство</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Метод уведомлений</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">Ожидание авторизации</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Привязка устройства авторизована</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">Пожалуйста, убедитесь, что слова ниже соответствуют тем, которые показаны на вашем другом устройстве.</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">Ваше устройство успешно привязано</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">Ожидание устройства</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">Запрос на привязывание получен</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">Ссылка на авторизирующее устройство</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">Загрузите Session на другое устройство и нажмите «Привязать к существующей учетной записи» в нижней части целевого экрана. Если у вас уже есть учетная запись на другом устройстве, вам придется сначала удалить ту учетную запись.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">Пожалуйста, убедитесь, что слова ниже соответствуют тем, которые показаны на вашем другом устройстве.</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">Пожалуйста, подождите, пока будет создана ссылка на устройство. Это может занять до минуты.</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">Авторизация</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">Сменить имя</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">Отключить устройство</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">Введите имя</string>
|
||||
|
||||
<string name="dialog_seed_title">Ваша секретная фраза</string>
|
||||
<string name="dialog_seed_explanation">Это ваша секретная фраза. С ее помощью вы можете восстановить или перенести свой Session ID на новое устройство.</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">Очистить все данные</string>
|
||||
<string name="dialog_clear_all_data_explanation">Это навсегда удалит ваши сообщения, сессии и контакты.</string>
|
||||
|
||||
<string name="activity_qr_code_title">QR-код</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">Посмотреть мой QR-код</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">Сканировать QR-код</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">Отсканируйте QR-код другого человека, чтобы начать с ним разговор</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">Это ваш QR-код. Другие пользователи могут сканировать его, чтобы начать диалог с вами.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Поделиться QR-кодом</string>
|
||||
|
||||
<string name="session_reset_banner_message">Хотите восстановить сессию с %s?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Отклонить</string>
|
||||
<string name="session_reset_banner_restore_button_title">Восстановить</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">Контакты</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">Закрытые группы</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">Открытые группы</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1266,5 +1266,185 @@
|
||||
<string name="prompt_passphrase_activity__tap_to_unlock">轻触解锁</string>
|
||||
<string name="RegistrationLockDialog_reminder">提醒:</string>
|
||||
<string name="recipient_preferences__about">关于</string>
|
||||
<!--EOF-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
<string name="continue_2">继续</string>
|
||||
<string name="copy">复制</string>
|
||||
<string name="invalid_url">无效的网址</string>
|
||||
<string name="copied_to_clipboard">复制到剪贴板</string>
|
||||
<string name="device_linking_failed">无法链接设备。</string>
|
||||
<string name="next">下一步</string>
|
||||
<string name="share">分享</string>
|
||||
<string name="invalid_session_id">无效的Session ID</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="your_session_id">您的Session ID</string>
|
||||
|
||||
<string name="activity_landing_title_2">您的Session从这里开始...</string>
|
||||
<string name="activity_landing_register_button_title">注册Session ID</string>
|
||||
<string name="activity_landing_restore_button_title">继续使用您的Session ID</string>
|
||||
<string name="activity_landing_link_button_title">链接到现有帐号</string>
|
||||
<string name="activity_landing_device_unlinked_dialog_title">您的设备已成功断开链接</string>
|
||||
|
||||
<string name="view_fake_chat_bubble_1">什么是Session?</string>
|
||||
<string name="view_fake_chat_bubble_2">Session是一个去中心化的加密消息应用。</string>
|
||||
<string name="view_fake_chat_bubble_3">所以Session不会收集我的个人信息或对话原始数据?怎么做到的?。</string>
|
||||
<string name="view_fake_chat_bubble_4">通过结合高效的匿名路由和端到端的加密技术。</string>
|
||||
<string name="view_fake_chat_bubble_5">好朋友就要与朋友使用能够保证信息安全的聊天工具,不用谢啦</string>
|
||||
|
||||
<string name="activity_register_title">向您的新Session ID打个招呼吧</string>
|
||||
<string name="activity_register_explanation">Session ID是其他用户需要与您聊天时使用的独一无二的地址。与您的真实身份无关,Session ID的设计是完全是匿名和私有的。</string>
|
||||
<string name="activity_register_public_key_copied_message">复制到剪贴板</string>
|
||||
|
||||
<string name="activity_restore_title">恢复您的帐号</string>
|
||||
<string name="activity_restore_explanation">在您重新登陆并需要恢复账户时,请输入您注册帐号时的恢复口令。</string>
|
||||
<string name="activity_restore_seed_edit_text_hint">输入您的恢复口令</string>
|
||||
|
||||
<string name="activity_link_device_title">链接设备</string>
|
||||
<string name="activity_link_device_enter_session_id_tab_title">输入Session ID</string>
|
||||
<string name="activity_link_device_scan_qr_code_tab_title">扫描二维码</string>
|
||||
<string name="activity_link_device_scan_qr_code_explanation">在您的设备上导航到“设置”>“设备”>“链接设备”,然后扫描出现的二维码以开始链接过程。</string>
|
||||
|
||||
<string name="fragment_enter_session_id_title">链接您的设备</string>
|
||||
<string name="fragment_enter_session_id_explanation">在您的另一个设备上导航到“设置”>“设备” >“链接设备”,然后在此处输入Session ID以开始链接过程。</string>
|
||||
<string name="fragment_enter_session_id_edit_text_hint">输入Session ID</string>
|
||||
|
||||
<string name="activity_display_name_title_2">选择您的显示名称</string>
|
||||
<string name="activity_display_name_explanation">使用Session时,这就是您的名字。它可以是您的真实姓名,别名或您喜欢的其他任何名称。</string>
|
||||
<string name="activity_display_name_edit_text_hint">输入显示名称</string>
|
||||
<string name="activity_display_name_display_name_missing_error">请选择一个显示名称</string>
|
||||
<string name="activity_display_name_display_name_invalid_error">请选择一个仅包含 az,AZ,0-9 和_字符的显示名称</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">请选择一个较短的显示名称</string>
|
||||
|
||||
<string name="activity_pn_mode_recommended_option_tag">推荐的选项</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">请选择一个选项</string>
|
||||
|
||||
<string name="activity_home_empty_state_message">您还没有任何联系人</string>
|
||||
<string name="activity_home_empty_state_button_title">开始对话</string>
|
||||
<string name="activity_home_leave_group_dialog_message">您确定要离开这个群组吗?</string>
|
||||
<string name="activity_home_leaving_group_failed_message">无法离开群组</string>
|
||||
<string name="activity_home_delete_conversation_dialog_message">您确定要删除此对话吗?</string>
|
||||
<string name="activity_home_conversation_deleted_message">对话已删除</string>
|
||||
|
||||
<string name="activity_seed_title">您的恢复口令</string>
|
||||
<string name="activity_seed_title_2">这里是您的恢复口令</string>
|
||||
<string name="activity_seed_explanation">您的恢复口令是Session ID的主密钥 - 如果您无法访问您的现有设备,则可以使用它在其他设备上恢复Session ID。将您的恢复口令存储在安全的地方,不要将其提供给任何人。</string>
|
||||
<string name="activity_seed_reveal_button_title">长按显示内容</string>
|
||||
|
||||
<string name="view_seed_reminder_subtitle_1">保存恢复短语以保护您的帐号安全</string>
|
||||
<string name="view_seed_reminder_subtitle_2">点击并按住遮盖住的单词以显示您的恢复短语,然后安全地存储它以保护Session ID。</string>
|
||||
<string name="view_seed_reminder_subtitle_3">确保将恢复短语存储在安全的地方</string>
|
||||
|
||||
<string name="activity_path_title">路径</string>
|
||||
<string name="activity_path_explanation">Session会通过Session的分散网络中的多个服务节点跳转消息以隐藏IP。以下是国家您目前的消息连接跳转服务节点所在地:</string>
|
||||
<string name="activity_path_device_row_title">您</string>
|
||||
<string name="activity_path_guard_node_row_title">入口节点</string>
|
||||
<string name="activity_path_service_node_row_title">服务节点</string>
|
||||
<string name="activity_path_destination_row_title">目的地</string>
|
||||
<string name="activity_path_learn_more_button_title">了解更多</string>
|
||||
|
||||
<string name="activity_create_private_chat_title">新建私人聊天</string>
|
||||
<string name="activity_create_private_chat_enter_session_id_tab_title">输入Session ID</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_tab_title">扫描二维码</string>
|
||||
<string name="activity_create_private_chat_scan_qr_code_explanation">扫描另一用户的二维码以开始使用Session。您可以在帐号设置中点击二维码图标找到二维码。</string>
|
||||
|
||||
<string name="fragment_enter_public_key_edit_text_hint">输入对方的Session ID</string>
|
||||
<string name="fragment_enter_public_key_explanation">用户可以通过进入帐号设置并点击共享Session ID来分享自己的Session ID,或通过共享其二维码来分享其Session ID。</string>
|
||||
|
||||
<string name="fragment_scan_qr_code_camera_access_explanation">Session需要摄像头访问权限才能扫描二维码</string>
|
||||
<string name="fragment_scan_qr_code_grant_camera_access_button_title">授予摄像头访问权限</string>
|
||||
|
||||
<string name="activity_create_closed_group_title">创建私密群组</string>
|
||||
<string name="activity_create_closed_group_edit_text_hint">输入群组名称</string>
|
||||
<string name="activity_create_closed_group_explanation">私密群组最多支持 10 位成员,并提供与一对一对话相同的隐私保护。</string>
|
||||
<string name="activity_create_closed_group_empty_state_message">您还没有任何联系人</string>
|
||||
<string name="activity_create_closed_group_empty_state_button_title">开始对话</string>
|
||||
<string name="activity_create_closed_group_group_name_missing_error">请输入群组名称</string>
|
||||
<string name="activity_create_closed_group_group_name_too_long_error">请输入较短的群组名称</string>
|
||||
<string name="activity_create_closed_group_not_enough_group_members_error">请选择至少 2 位小组成员</string>
|
||||
<string name="activity_create_closed_group_too_many_group_members_error">私密群组成员不得超过 10 个</string>
|
||||
<string name="activity_create_closed_group_invalid_session_id_error">您群组中的一位成员的Session ID无效</string>
|
||||
|
||||
<string name="activity_join_public_chat_title">加入公开群组</string>
|
||||
<string name="activity_join_public_chat_error">无法加入群组</string>
|
||||
<string name="activity_join_public_chat_enter_group_url_tab_title">公开群组网址</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_tab_title">扫描二维码</string>
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">扫描您想加入的公开群组的二维码</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">输入一个公开群组网址</string>
|
||||
|
||||
<string name="activity_settings_title">设置</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">输入显示的名称</string>
|
||||
<string name="activity_settings_display_name_missing_error">请选择一个显示名称</string>
|
||||
<string name="activity_settings_invalid_display_name_error">请选择一个仅包含 az,AZ,0-9 和 _ 字符的显示名称</string>
|
||||
<string name="activity_settings_display_name_too_long_error">请选择一个较短的显示名称</string>
|
||||
<string name="activity_settings_privacy_button_title">隐私</string>
|
||||
<string name="activity_settings_notifications_button_title">通知</string>
|
||||
<string name="activity_settings_chats_button_title">聊天</string>
|
||||
<string name="activity_settings_devices_button_title">设备</string>
|
||||
<string name="activity_settings_recovery_phrase_button_title">恢复口令</string>
|
||||
<string name="activity_settings_clear_all_data_button_title">清除数据</string>
|
||||
|
||||
<string name="activity_notification_settings_title">通知</string>
|
||||
<string name="activity_notification_settings_style_section_title">通知风格类型</string>
|
||||
<string name="activity_notification_settings_content_section_title">通知内容</string>
|
||||
|
||||
<string name="activity_privacy_settings_title">隐私</string>
|
||||
|
||||
<string name="activity_chat_settings_title">聊天</string>
|
||||
|
||||
<string name="activity_linked_devices_title">设备</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_title">达到设备限制</string>
|
||||
<string name="activity_linked_devices_multi_device_limit_reached_dialog_explanation">当前不允许链接多个设备。</string>
|
||||
<string name="activity_linked_devices_unlinking_failed_message">无法断开链接设备。</string>
|
||||
<string name="activity_linked_devices_unlinking_successful_message">您的设备已成功断开链接</string>
|
||||
<string name="activity_linked_devices_linking_failed_message">无法链接设备。</string>
|
||||
<string name="activity_linked_devices_empty_state_message">您尚未链接任何设备</string>
|
||||
<string name="activity_linked_devices_empty_state_button_title">链接设备(测试版)</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">通知选项</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">等待授权</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">设备链接授权</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_1">请检查以下单词是否与您其他设备上显示的单词匹配。</string>
|
||||
<string name="dialog_link_device_slave_mode_explanation_2">您的设备已成功链接</string>
|
||||
|
||||
<string name="dialog_link_device_master_mode_title_1">等待设备</string>
|
||||
<string name="dialog_link_device_master_mode_title_2">收到链接请求</string>
|
||||
<string name="dialog_link_device_master_mode_title_3">授权设备链接</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_1">在其他设备上下载Session,然后点击登陆页面屏幕底部的“链接到现有帐号”。如果您的其他设备上已有一个帐号,则必须先删除已有帐号。</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_2">请检查以下单词是否与您其他设备上显示的单词匹配。</string>
|
||||
<string name="dialog_link_device_master_mode_explanation_3">创建设备关联时,请耐心等待。这可能需要一分钟的时间。</string>
|
||||
<string name="dialog_link_device_master_mode_authorize_button_title">授权</string>
|
||||
|
||||
<string name="fragment_device_list_bottom_sheet_change_name_button_title">更换名字</string>
|
||||
<string name="fragment_device_list_bottom_sheet_unlink_device_button_title">断开设备链接</string>
|
||||
|
||||
<string name="dialog_edit_device_name_edit_text_hint">输入名字</string>
|
||||
|
||||
<string name="dialog_seed_title">您的恢复口令</string>
|
||||
<string name="dialog_seed_explanation">这是您的恢复口令。有了它,您可以将Session ID还原或迁移到新设备上。</string>
|
||||
|
||||
<string name="dialog_clear_all_data_title">清除所有数据</string>
|
||||
<string name="dialog_clear_all_data_explanation">这将永久删除您的消息、对话和联系人。</string>
|
||||
|
||||
<string name="activity_qr_code_title">二维码</string>
|
||||
<string name="activity_qr_code_view_my_qr_code_tab_title">查看我的二维码</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_tab_title">扫描二维码</string>
|
||||
<string name="activity_qr_code_view_scan_qr_code_explanation">扫描对方的二维码,与他们开始对话</string>
|
||||
|
||||
<string name="fragment_view_my_qr_code_explanation">这是您的二维码。其他用户可以对其进行扫描以开始对话。</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">分享二维码</string>
|
||||
|
||||
<string name="session_reset_banner_message">您要恢复与%s的对话吗?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">解散</string>
|
||||
<string name="session_reset_banner_restore_button_title">恢复</string>
|
||||
|
||||
<string name="fragment_contact_selection_contacts_title">联系人</string>
|
||||
<string name="fragment_contact_selection_closed_groups_title">私密群组</string>
|
||||
<string name="fragment_contact_selection_open_groups_title">公开群组</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1655,7 +1655,6 @@
|
||||
|
||||
|
||||
<!-- Session -->
|
||||
|
||||
<string name="continue_2">Continue</string>
|
||||
<string name="copy">Copy</string>
|
||||
<string name="invalid_url">Invalid URL</string>
|
||||
@ -1703,12 +1702,6 @@
|
||||
<string name="activity_display_name_display_name_invalid_error">Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters</string>
|
||||
<string name="activity_display_name_display_name_too_long_error">Please pick a shorter display name</string>
|
||||
|
||||
<string name="activity_pn_mode_title">Push Notifications</string>
|
||||
<string name="activity_pn_mode_explanation">There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.</string>
|
||||
<string name="activity_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||
<string name="activity_pn_mode_fcm_option_explanation">Session will use the Firebase Cloud Messaging service to receive push notifications. You\'ll be notified of new messages reliably and immediately. Using FCM means that your IP address and device token will be exposed to Google. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.</string>
|
||||
<string name="activity_pn_mode_background_polling_option_title">Background Polling</string>
|
||||
<string name="activity_pn_mode_background_polling_option_explanation">Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed.</string>
|
||||
<string name="activity_pn_mode_recommended_option_tag">Recommended</string>
|
||||
<string name="activity_pn_mode_no_option_picked_dialog_title">Please Pick an Option</string>
|
||||
|
||||
@ -1719,17 +1712,6 @@
|
||||
<string name="activity_home_delete_conversation_dialog_message">Are you sure you want to delete this conversation?</string>
|
||||
<string name="activity_home_conversation_deleted_message">Conversation deleted</string>
|
||||
|
||||
<string name="sheet_pn_mode_title">Push Notifications</string>
|
||||
<string name="sheet_pn_mode_explanation">Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose.</string>
|
||||
<string name="sheet_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||
<string name="sheet_pn_mode_fcm_option_explanation">Session will use the Firebase Cloud Messaging service to receive push notifications. You\'ll be notified of new messages reliably and immediately. Using FCM means that your IP address and device token will be exposed to Google. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_title">Background Polling</string>
|
||||
<string name="sheet_pn_mode_background_polling_option_explanation">Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed.</string>
|
||||
<string name="sheet_pn_mode_recommended_option_tag">Recommended</string>
|
||||
<string name="sheet_pn_mode_no_option_picked_dialog_title">Please Pick an Option</string>
|
||||
<string name="sheet_pn_mode_confirm_button_title">Confirm</string>
|
||||
<string name="sheet_pn_mode_skip_button_title">Skip</string>
|
||||
|
||||
<string name="activity_seed_title">Your Recovery Phrase</string>
|
||||
<string name="activity_seed_title_2">Meet your recovery phrase</string>
|
||||
<string name="activity_seed_explanation">Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\’t give it to anyone.</string>
|
||||
@ -1795,7 +1777,6 @@
|
||||
<string name="activity_join_public_chat_scan_qr_code_explanation">Scan the QR code of the open group you\'d like to join</string>
|
||||
|
||||
<string name="fragment_enter_chat_url_edit_text_hint">Enter an open group URL</string>
|
||||
<string name="fragment_enter_chat_url_privacy_warning">Open groups can be joined by anyone and do not provide full privacy protection</string>
|
||||
|
||||
<string name="activity_settings_title">Settings</string>
|
||||
<string name="activity_settings_display_name_edit_text_hint">Enter a display name</string>
|
||||
@ -1827,8 +1808,6 @@
|
||||
<string name="activity_linked_devices_empty_state_button_title">Link a Device (Beta)</string>
|
||||
|
||||
<string name="preferences_notifications_strategy_category_title">Notification Strategy</string>
|
||||
<string name="preferences_notifications_use_fcm_option_title">Use FCM</string>
|
||||
<string name="preferences_notifications_use_fcm_option_explanation">Using Firebase Cloud Messaging allows for more reliable push notifications, but exposes your IP and device token to Google and Loki.</string>
|
||||
|
||||
<string name="dialog_link_device_slave_mode_title_1">Waiting for Authorization</string>
|
||||
<string name="dialog_link_device_slave_mode_title_2">Device Link Authorized</string>
|
||||
@ -1862,16 +1841,6 @@
|
||||
<string name="fragment_view_my_qr_code_explanation">This is your QR code. Other users can scan it to start a session with you.</string>
|
||||
<string name="fragment_view_my_qr_code_share_title">Share QR Code</string>
|
||||
|
||||
<string name="view_friend_request_accept_button_title">Accept</string>
|
||||
<string name="view_friend_request_reject_button_title">Decline</string>
|
||||
<string name="view_friend_request_incoming_pending_message">%1$s sent you a session request</string>
|
||||
<string name="view_friend_request_incoming_accepted_message">You\'ve accepted %1$s\'s session request</string>
|
||||
<string name="view_friend_request_incoming_declined_message">You\'ve declined %1$s\'s session request</string>
|
||||
<string name="view_friend_request_incoming_expired_message">%1$s\'s session request has expired</string>
|
||||
<string name="view_friend_request_outgoing_pending_message">You\'ve sent %1$s a session request</string>
|
||||
<string name="view_friend_request_outgoing_accepted_message">%1$s accepted your session request</string>
|
||||
<string name="view_friend_request_outgoing_expired_message">Your session request to %1$s has expired</string>
|
||||
|
||||
<string name="session_reset_banner_message">Would you like to restore your session with %s?</string>
|
||||
<string name="session_reset_banner_dismiss_button_title">Dismiss</string>
|
||||
<string name="session_reset_banner_restore_button_title">Restore</string>
|
||||
|
@ -139,6 +139,7 @@
|
||||
<item name="android:textSize">@dimen/large_font_size</item>
|
||||
<item name="android:textColor">@color/text</item>
|
||||
<item name="android:fontFamily">@font/space_mono_regular</item>
|
||||
<item name="android:textAlignment">viewStart</item>
|
||||
</style>
|
||||
|
||||
<style name="SessionEditText">
|
||||
@ -150,6 +151,7 @@
|
||||
<item name="android:textSize">@dimen/small_font_size</item>
|
||||
<item name="android:textColor">@color/text</item>
|
||||
<item name="android:textCursorDrawable">@drawable/session_edit_text_cursor</item>
|
||||
<item name="android:textAlignment">viewStart</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
</style>
|
||||
|
||||
@ -162,6 +164,7 @@
|
||||
<item name="android:textSize">@dimen/small_font_size</item>
|
||||
<item name="android:textColor">@color/text</item>
|
||||
<item name="android:textCursorDrawable">@drawable/session_edit_text_cursor</item>
|
||||
<item name="android:textAlignment">viewStart</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
</style>
|
||||
|
||||
|
@ -26,8 +26,8 @@
|
||||
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
|
||||
android:dependency="pref_key_enable_notifications"
|
||||
android:key="pref_key_use_fcm"
|
||||
android:title="@string/preferences_notifications_use_fcm_option_title"
|
||||
android:summary="@string/preferences_notifications_use_fcm_option_explanation"
|
||||
android:title="Use Fast Mode"
|
||||
android:summary="You’ll be notified of new messages reliably and immediately using Google’s notification servers. The contents of your messages, and who you’re messaging, are never exposed to Google."
|
||||
android:defaultValue="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
|
||||
@ -60,17 +61,18 @@ import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
||||
import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager;
|
||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
||||
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||
import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
|
||||
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushSessionRequestMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
|
||||
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
@ -89,27 +91,29 @@ import org.webrtc.PeerConnectionFactory;
|
||||
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
||||
import org.whispersystems.signalservice.loki.api.LokiPoller;
|
||||
import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement;
|
||||
import org.whispersystems.signalservice.loki.api.LokiSwarmAPI;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
|
||||
import org.whispersystems.signalservice.loki.api.p2p.LokiP2PAPI;
|
||||
import org.whispersystems.signalservice.loki.api.p2p.LokiP2PAPIDelegate;
|
||||
import org.whispersystems.signalservice.loki.api.Poller;
|
||||
import org.whispersystems.signalservice.loki.api.PushNotificationAcknowledgement;
|
||||
import org.whispersystems.signalservice.loki.api.SnodeAPI;
|
||||
import org.whispersystems.signalservice.loki.api.SwarmAPI;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
|
||||
import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPI;
|
||||
import org.whispersystems.signalservice.loki.api.shelved.p2p.LokiP2PAPIDelegate;
|
||||
import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate;
|
||||
import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -148,9 +152,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
private PersistentLogger persistentLogger;
|
||||
|
||||
// Loki
|
||||
public LokiPoller lokiPoller = null;
|
||||
public LokiPublicChatManager lokiPublicChatManager = null;
|
||||
private LokiPublicChatAPI lokiPublicChatAPI = null;
|
||||
public MessageNotifier messageNotifier = null;
|
||||
public Poller poller = null;
|
||||
public PublicChatManager publicChatManager = null;
|
||||
private PublicChatAPI publicChatAPI = null;
|
||||
public Broadcaster broadcaster = null;
|
||||
public SignalCommunicationModule communicationModule;
|
||||
|
||||
@ -173,16 +178,16 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
// Loki
|
||||
// ========
|
||||
messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier());
|
||||
broadcaster = new Broadcaster(this);
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
|
||||
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
LokiSessionResetImplementation sessionResetImpl = new LokiSessionResetImplementation(this);
|
||||
SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this);
|
||||
if (userPublicKey != null) {
|
||||
LokiSwarmAPI.Companion.configureIfNeeded(apiDB);
|
||||
LokiAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
||||
FriendRequestProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
|
||||
SwarmAPI.Companion.configureIfNeeded(apiDB);
|
||||
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
||||
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
|
||||
SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
|
||||
SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
|
||||
@ -190,15 +195,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
MultiDeviceProtocol.Companion.configureIfNeeded(apiDB);
|
||||
SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, threadDB, this);
|
||||
setUpP2PAPIIfNeeded();
|
||||
LokiPushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
|
||||
PushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
|
||||
if (setUpStorageAPIIfNeeded()) {
|
||||
if (userPublicKey != null) {
|
||||
Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey);
|
||||
LokiFileServerAPI.shared.setDeviceLinks(deviceLinks);
|
||||
FileServerAPI.shared.setDeviceLinks(deviceLinks);
|
||||
}
|
||||
}
|
||||
resubmitProfilePictureIfNeeded();
|
||||
lokiPublicChatManager = new LokiPublicChatManager(this);
|
||||
publicChatManager = new PublicChatManager(this);
|
||||
updateOpenGroupProfilePicturesIfNeeded();
|
||||
registerForFCMIfNeeded(false);
|
||||
// ========
|
||||
@ -222,8 +227,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
executePendingContactSync();
|
||||
KeyCachingService.onAppForegrounded(this);
|
||||
// Loki
|
||||
if (poller != null) { poller.setCaughtUp(false); }
|
||||
startPollingIfNeeded();
|
||||
lokiPublicChatManager.startPollersIfNeeded();
|
||||
publicChatManager.markAllAsNotCaughtUp();
|
||||
publicChatManager.startPollersIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -231,10 +238,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
isAppVisible = false;
|
||||
Log.i(TAG, "App is no longer visible.");
|
||||
KeyCachingService.onAppBackgrounded(this);
|
||||
MessageNotifier.setVisibleThread(-1);
|
||||
messageNotifier.setVisibleThread(-1);
|
||||
// Loki
|
||||
if (lokiPoller != null) { lokiPoller.stopIfNeeded(); }
|
||||
if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); }
|
||||
if (poller != null) { poller.stopIfNeeded(); }
|
||||
if (publicChatManager != null) { publicChatManager.stopPollers(); }
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -275,15 +282,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
// Loki
|
||||
public @Nullable LokiPublicChatAPI getLokiPublicChatAPI() {
|
||||
if (lokiPublicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return lokiPublicChatAPI; }
|
||||
public @Nullable PublicChatAPI getPublicChatAPI() {
|
||||
if (publicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return publicChatAPI; }
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
if (userPublicKey== null) { return lokiPublicChatAPI; }
|
||||
if (userPublicKey== null) { return publicChatAPI; }
|
||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
||||
lokiPublicChatAPI = new LokiPublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB);
|
||||
return lokiPublicChatAPI;
|
||||
publicChatAPI = new PublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB);
|
||||
return publicChatAPI;
|
||||
}
|
||||
|
||||
private void initializeSecurityProvider() {
|
||||
@ -437,8 +444,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base)));
|
||||
}
|
||||
|
||||
private static class ProviderInitializationException extends RuntimeException {
|
||||
}
|
||||
private static class ProviderInitializationException extends RuntimeException { }
|
||||
|
||||
// region Loki
|
||||
public boolean setUpStorageAPIIfNeeded() {
|
||||
@ -447,7 +453,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
boolean isDebugMode = BuildConfig.DEBUG;
|
||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||
LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
LokiFileServerAPI.Companion.configure(isDebugMode, userPublicKey, userPrivateKey, apiDB);
|
||||
FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -484,14 +490,18 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
private void setUpPollingIfNeeded() {
|
||||
if (lokiPoller != null) return;
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
if (userPublicKey == null) return;
|
||||
if (poller != null) {
|
||||
SnodeAPI.shared.setUserPublicKey(userPublicKey);
|
||||
poller.setUserPublicKey(userPublicKey);
|
||||
return;
|
||||
}
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
Context context = this;
|
||||
LokiSwarmAPI.Companion.configureIfNeeded(apiDB);
|
||||
LokiAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
||||
lokiPoller = new LokiPoller(userPublicKey, apiDB, protos -> {
|
||||
SwarmAPI.Companion.configureIfNeeded(apiDB);
|
||||
SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
|
||||
poller = new Poller(userPublicKey, apiDB, protos -> {
|
||||
for (SignalServiceProtos.Envelope proto : protos) {
|
||||
new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto), false);
|
||||
}
|
||||
@ -501,7 +511,12 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
public void startPollingIfNeeded() {
|
||||
setUpPollingIfNeeded();
|
||||
if (lokiPoller != null) { lokiPoller.startIfNeeded(); }
|
||||
if (poller != null) { poller.startIfNeeded(); }
|
||||
}
|
||||
|
||||
public void stopPolling() {
|
||||
if (poller == null) { return; }
|
||||
poller.stopIfNeeded();
|
||||
}
|
||||
|
||||
private void resubmitProfilePictureIfNeeded() {
|
||||
@ -516,7 +531,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
try {
|
||||
File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey));
|
||||
StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length());
|
||||
LokiFileServerAPI.shared.uploadProfilePicture(LokiFileServerAPI.shared.getServer(), profileKey, stream, () -> {
|
||||
FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> {
|
||||
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
|
||||
TextSecurePreferences.setProfileAvatarId(this, new SecureRandom().nextInt());
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey);
|
||||
@ -530,9 +545,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
public void updateOpenGroupProfilePicturesIfNeeded() {
|
||||
AsyncTask.execute(() -> {
|
||||
LokiPublicChatAPI publicChatAPI = null;
|
||||
PublicChatAPI publicChatAPI = null;
|
||||
try {
|
||||
publicChatAPI = getLokiPublicChatAPI();
|
||||
publicChatAPI = getPublicChatAPI();
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
@ -555,6 +570,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
|
||||
public void clearData() {
|
||||
String token = TextSecurePreferences.getFCMToken(this);
|
||||
if (token != null && !token.isEmpty()) {
|
||||
LokiPushNotificationManager.unregister(token, this);
|
||||
}
|
||||
boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this);
|
||||
TextSecurePreferences.clearAll(this);
|
||||
TextSecurePreferences.setWasUnlinked(this, wasUnlinked);
|
||||
@ -571,11 +590,39 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
|
||||
public boolean hasSentSessionRequestExpired(@NotNull String publicKey) {
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
Long timestamp = apiDB.getSessionRequestSentTimestamp(publicKey);
|
||||
if (timestamp != null) {
|
||||
long expiration = timestamp + TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest);
|
||||
return new Date().getTime() > expiration;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSessionRequest(@NotNull String publicKey) {
|
||||
DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime());
|
||||
EphemeralMessage sessionRequest = EphemeralMessage.createSessionRequest(publicKey);
|
||||
jobManager.add(new PushEphemeralMessageSendJob(sessionRequest));
|
||||
public void sendSessionRequestIfNeeded(@NotNull String publicKey) {
|
||||
// It's never necessary to establish a session with self
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
if (publicKey.equals(userPublicKey)) { return; }
|
||||
// Check that we don't already have a session
|
||||
SignalProtocolAddress address = new SignalProtocolAddress(publicKey, SignalServiceAddress.DEFAULT_DEVICE_ID);
|
||||
boolean hasSession = new TextSecureSessionStore(this).containsSession(address);
|
||||
if (hasSession) { return; }
|
||||
// Check that we didn't already send a session request
|
||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||
boolean hasSentSessionRequest = (apiDB.getSessionRequestSentTimestamp(publicKey) != null);
|
||||
boolean hasSentSessionRequestExpired = hasSentSessionRequestExpired(publicKey);
|
||||
if (hasSentSessionRequestExpired) {
|
||||
apiDB.setSessionRequestSentTimestamp(publicKey, 0);
|
||||
}
|
||||
if (hasSentSessionRequest && !hasSentSessionRequestExpired) { return; }
|
||||
// Send the session request
|
||||
long timestamp = new Date().getTime();
|
||||
apiDB.setSessionRequestSentTimestamp(publicKey, timestamp);
|
||||
PushSessionRequestMessageSendJob job = new PushSessionRequestMessageSendJob(publicKey, timestamp);
|
||||
jobManager.add(job);
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
Context context = ConversationListActivity.this;
|
||||
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setAllThreadsRead();
|
||||
|
||||
MessageNotifier.updateNotification(context);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
||||
MarkReadReceiver.process(context, messageIds);
|
||||
|
||||
return null;
|
||||
|
@ -324,8 +324,9 @@ public class ConversationListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations);
|
||||
MessageNotifier.updateNotification(getActivity());
|
||||
Context context = getActivity();
|
||||
DatabaseFactory.getThreadDatabase(context).deleteConversations(selectedConversations);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -542,9 +543,10 @@ public class ConversationListFragment extends Fragment
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId);
|
||||
|
||||
if (unreadCount > 0) {
|
||||
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(getActivity()).setRead(threadId, false);
|
||||
MessageNotifier.updateNotification(getActivity());
|
||||
MarkReadReceiver.process(getActivity(), messageIds);
|
||||
Context context = getActivity();
|
||||
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, false);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
||||
MarkReadReceiver.process(context, messageIds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,8 +555,9 @@ public class ConversationListFragment extends Fragment
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId);
|
||||
|
||||
if (unreadCount > 0) {
|
||||
DatabaseFactory.getThreadDatabase(getActivity()).incrementUnread(threadId, unreadCount);
|
||||
MessageNotifier.updateNotification(getActivity());
|
||||
Context context = getActivity();
|
||||
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, unreadCount);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
|
||||
|
@ -58,8 +58,8 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
@ -386,7 +386,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
||||
Context context = CreateProfileActivity.this;
|
||||
|
||||
TextSecurePreferences.setProfileName(context, name);
|
||||
LokiPublicChatAPI publicChatAPI = ApplicationContext.getInstance(context).getLokiPublicChatAPI();
|
||||
PublicChatAPI publicChatAPI = ApplicationContext.getInstance(context).getPublicChatAPI();
|
||||
if (publicChatAPI != null) {
|
||||
Set<String> servers = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChatServers();
|
||||
for (String server : servers) {
|
||||
@ -409,7 +409,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
||||
// Loki - Upload the profile photo here
|
||||
if (avatar != null) {
|
||||
Log.d("Loki", "Start uploading profile photo");
|
||||
LokiFileServerAPI storageAPI = LokiFileServerAPI.shared;
|
||||
FileServerAPI storageAPI = FileServerAPI.shared;
|
||||
LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar, () -> {
|
||||
TextSecurePreferences.setLastProfilePictureUpload(CreateProfileActivity.this, new Date().getTime());
|
||||
return Unit.INSTANCE;
|
||||
|
@ -176,7 +176,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
MessageNotifier.updateNotification(context);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
@ -322,7 +322,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
|
||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
|
||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_CONTACTS);
|
||||
startActivityForResult(intent, PICK_CONTACT);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_CONTACTS);
|
||||
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
|
||||
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
|
||||
|
@ -133,13 +133,13 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setTitle("Message Details");
|
||||
|
||||
MessageNotifier.setVisibleThread(threadId);
|
||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
MessageNotifier.setVisibleThread(-1L);
|
||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -197,7 +197,7 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
|
||||
|
||||
long threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(conversationRecipient);
|
||||
String senderHexEncodedPublicKey = author.getAddress().serialize();
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID);
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID);
|
||||
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
|
||||
quoteeDisplayName = TextSecurePreferences.getProfileName(getContext());
|
||||
} else if (publicChat != null) {
|
||||
|
@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -84,8 +84,9 @@ public class TypingStatusSender {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
|
||||
// Loki - Check whether we want to send a typing indicator to this user
|
||||
if (!SessionMetaProtocol.shouldSendTypingIndicator(recipient, context)) { return; }
|
||||
if (recipient != null && !SessionMetaProtocol.shouldSendTypingIndicator(recipient.getAddress())) { return; }
|
||||
// Loki - Take into account multi device
|
||||
if (recipient == null) { return; }
|
||||
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize());
|
||||
for (String device : linkedDevices) {
|
||||
Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false);
|
||||
|
@ -68,7 +68,6 @@ import android.view.View.OnFocusChangeListener;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
@ -81,7 +80,6 @@ import com.annimon.stream.Stream;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ExpirationDialog;
|
||||
import org.thoughtcrime.securesms.GroupCreateActivity;
|
||||
@ -160,10 +158,8 @@ import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
|
||||
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
|
||||
import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
|
||||
import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
@ -188,7 +184,6 @@ import org.thoughtcrime.securesms.mms.StickerSlide;
|
||||
import org.thoughtcrime.securesms.mms.TextSlide;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
|
||||
@ -228,12 +223,11 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.whispersystems.libsignal.InvalidMessageException;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -274,8 +268,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
ComposeText.CursorPositionChangedListener,
|
||||
ConversationSearchBottomBar.EventListener,
|
||||
StickerKeyboardProvider.StickerEventListener,
|
||||
LokiThreadDatabaseDelegate,
|
||||
FriendRequestViewDelegate
|
||||
LokiThreadDatabaseDelegate
|
||||
{
|
||||
private static final String TAG = ConversationActivity.class.getSimpleName();
|
||||
|
||||
@ -357,14 +350,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
// Message status bar
|
||||
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
|
||||
private String messageStatus = null;
|
||||
private String messageStatus = null;
|
||||
|
||||
// Mentions
|
||||
private View mentionCandidateSelectionViewContainer;
|
||||
private View mentionCandidateSelectionViewContainer;
|
||||
private MentionCandidateSelectionView mentionCandidateSelectionView;
|
||||
private int currentMentionStartIndex = -1;
|
||||
private ArrayList<Mention> mentions = new ArrayList<>();
|
||||
private String oldText = "";
|
||||
private int currentMentionStartIndex = -1;
|
||||
private ArrayList<Mention> mentions = new ArrayList<>();
|
||||
private String oldText = "";
|
||||
|
||||
// Restoration
|
||||
protected SessionRestoreBannerView sessionRestoreBannerView;
|
||||
@ -388,7 +381,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
getWindow().getDecorView().setBackgroundColor(color);
|
||||
|
||||
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), dynamicLanguage.getCurrentLocale());
|
||||
fragment.friendRequestViewDelegate = this;
|
||||
|
||||
registerMessageStatusObserver("calculatingPoW");
|
||||
registerMessageStatusObserver("contactingNetwork");
|
||||
@ -439,7 +431,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
mentionCandidateSelectionView.setOnMentionCandidateSelected( mentionCandidate -> {
|
||||
mentions.add(mentionCandidate);
|
||||
String oldText = composeText.getText().toString();
|
||||
String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName();
|
||||
String newText = oldText.substring(0, currentMentionStartIndex) + "@" + mentionCandidate.getDisplayName() + " ";
|
||||
composeText.setText(newText);
|
||||
composeText.setSelection(newText.length());
|
||||
currentMentionStartIndex = -1;
|
||||
@ -466,9 +458,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this);
|
||||
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
if (publicChat != null) {
|
||||
ApplicationContext.getInstance(this).getLokiPublicChatAPI().getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success( displayName -> {
|
||||
ApplicationContext.getInstance(this).getPublicChatAPI().getChannelInfo(publicChat.getChannel(), publicChat.getServer()).success(displayName -> {
|
||||
updateSubtitleTextView();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
@ -551,12 +543,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
setGroupShareProfileReminder(recipient);
|
||||
calculateCharactersRemaining();
|
||||
|
||||
MessageNotifier.setVisibleThread(threadId);
|
||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
|
||||
markThreadAsRead();
|
||||
|
||||
DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this);
|
||||
|
||||
updateInputPanel();
|
||||
inputPanel.setHint("Message");
|
||||
|
||||
updateSessionRestoreBanner();
|
||||
|
||||
@ -566,7 +558,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
MessageNotifier.setVisibleThread(-1L);
|
||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(-1L);
|
||||
if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
|
||||
inputPanel.onPause();
|
||||
|
||||
@ -762,10 +754,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
if (isSingleConversation()) {
|
||||
/*
|
||||
if (isSecureText) inflater.inflate(R.menu.conversation_callable_secure, menu);
|
||||
else inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
||||
*/
|
||||
if (recipient.isBlocked()) {
|
||||
inflater.inflate(R.menu.conversation_unblock, menu);
|
||||
} else {
|
||||
inflater.inflate(R.menu.conversation_block, menu);
|
||||
}
|
||||
} else if (isGroupConversation() && !isOpenGroupOrRSSFeed) {
|
||||
inflater.inflate(R.menu.conversation_group_options, menu);
|
||||
|
||||
@ -869,8 +862,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
|
||||
case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
|
||||
case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
|
||||
case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
|
||||
case R.id.menu_unblock: handleUnblock(); return true;
|
||||
case R.id.menu_block: handleBlock(); return true;
|
||||
case R.id.menu_view_media: handleViewMedia(); return true;
|
||||
case R.id.menu_add_shortcut: handleAddShortcut(); return true;
|
||||
case R.id.menu_search: handleSearch(); return true;
|
||||
@ -995,12 +990,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
int titleRes = R.string.ConversationActivity_unblock_this_contact_question;
|
||||
int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact;
|
||||
|
||||
if (recipient.isGroupRecipient()) {
|
||||
titleRes = R.string.ConversationActivity_unblock_this_group_question;
|
||||
bodyRes = R.string.ConversationActivity_unblock_this_group_description;
|
||||
}
|
||||
|
||||
//noinspection CodeBlock2Expr
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(titleRes)
|
||||
.setMessage(bodyRes)
|
||||
@ -1079,6 +1068,33 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void handleBlock() {
|
||||
int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question;
|
||||
int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact;
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(titleRes)
|
||||
.setMessage(bodyRes)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
||||
.setBlocked(recipient, true);
|
||||
|
||||
ApplicationContext.getInstance(ConversationActivity.this)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceBlockedUpdateJob());
|
||||
|
||||
Util.runOnMain(() -> finish());
|
||||
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleViewMedia() {
|
||||
Intent intent = new Intent(this, MediaOverviewActivity.class);
|
||||
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
@ -1323,6 +1339,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
this.isDefaultSms = isDefaultSms;
|
||||
this.isSecurityInitialized = true;
|
||||
|
||||
if (recipient == null || attachmentManager == null) { return; }
|
||||
|
||||
boolean isMediaMessage = recipient.isMmsGroupRecipient() || attachmentManager.isAttachmentPresent();
|
||||
|
||||
sendButton.resetAvailableTransports(isMediaMessage);
|
||||
@ -1711,7 +1729,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private void initializeResources() {
|
||||
if (recipient != null) recipient.removeListener(this);
|
||||
|
||||
recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
|
||||
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
||||
if (address == null) { finish(); return; }
|
||||
recipient = Recipient.from(this, address, true);
|
||||
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
|
||||
archived = getIntent().getBooleanExtra(IS_ARCHIVED_EXTRA, false);
|
||||
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
@ -2171,7 +2191,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
try {
|
||||
int startIndex = result.indexOf("@" + mention.getDisplayName());
|
||||
int endIndex = startIndex + mention.getDisplayName().length() + 1; // + 1 to include the @
|
||||
result = result.substring(0, startIndex) + "@" + mention.getHexEncodedPublicKey() + result.substring(endIndex);
|
||||
result = result.substring(0, startIndex) + "@" + mention.getPublicKey() + result.substring(endIndex);
|
||||
} catch (Exception exception) {
|
||||
Log.d("Loki", "Couldn't process mention due to error: " + exception.toString() + ".");
|
||||
}
|
||||
@ -2243,7 +2263,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
if (refreshFragment) {
|
||||
fragment.reload(recipient, threadId);
|
||||
MessageNotifier.setVisibleThread(threadId);
|
||||
ApplicationContext.getInstance(this).messageNotifier.setVisibleThread(threadId);
|
||||
}
|
||||
|
||||
fragment.scrollToBottom();
|
||||
@ -2252,26 +2272,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
updateLinkPreviewState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleThreadFriendRequestStatusChanged(long threadID) {
|
||||
if (recipient.isGroupRecipient()) { return; }
|
||||
boolean isUpdateNeeded = false;
|
||||
if (threadID == this.threadId) {
|
||||
isUpdateNeeded = true;
|
||||
} else {
|
||||
String thisThreadPublicKey = recipient.getAddress().serialize();
|
||||
Set<String> thisThreadAssociatedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(thisThreadPublicKey);
|
||||
Recipient changedThreadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
|
||||
String changedThreadPublicKey = changedThreadRecipient.getAddress().serialize();
|
||||
for (String device : thisThreadAssociatedDevices) {
|
||||
if (device.equals(changedThreadPublicKey)) { isUpdateNeeded = true; }
|
||||
}
|
||||
}
|
||||
if (isUpdateNeeded) {
|
||||
updateInputPanel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSessionRestoreDevicesChanged(long threadID) {
|
||||
if (threadID == this.threadId) {
|
||||
@ -2279,21 +2279,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInputPanel() {
|
||||
boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(this, recipient);
|
||||
Util.runOnMain(() -> {
|
||||
updateToggleButtonState();
|
||||
String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request";
|
||||
inputPanel.setHint(hint);
|
||||
inputPanel.setEnabled(shouldInputPanelBeEnabled);
|
||||
if (shouldInputPanelBeEnabled && inputPanel.getVisibility() == View.VISIBLE) {
|
||||
inputPanel.composeText.requestFocus();
|
||||
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage() {
|
||||
if (inputPanel.isRecordingInLockedMode()) {
|
||||
inputPanel.releaseRecordingLock();
|
||||
@ -2395,11 +2380,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
outgoingMessage = outgoingMessageCandidate;
|
||||
}
|
||||
|
||||
// Loki - Send a friend request if we're not yet friends with the user in question
|
||||
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
|
||||
outgoingMessage.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
|
||||
&& !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
|
||||
|
||||
if (clearComposeBox) {
|
||||
inputPanel.clearQuote();
|
||||
attachmentManager.clear(glideRequests, false);
|
||||
@ -2408,6 +2388,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
||||
|
||||
if (!recipient.isGroupRecipient()) {
|
||||
ApplicationContext.getInstance(this).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
|
||||
}
|
||||
|
||||
new AsyncTask<Void, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(Void... param) {
|
||||
@ -2448,14 +2432,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId);
|
||||
}
|
||||
|
||||
// Loki - Send a friend request if we're not yet friends with the user in question
|
||||
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
|
||||
message.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
|
||||
&& !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
|
||||
|
||||
silentlySetComposeText("");
|
||||
final long id = fragment.stageOutgoingMessage(message);
|
||||
|
||||
if (!recipient.isGroupRecipient()) {
|
||||
ApplicationContext.getInstance(this).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
|
||||
}
|
||||
|
||||
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(OutgoingTextMessage... messages) {
|
||||
@ -2482,13 +2465,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void updateToggleButtonState() {
|
||||
if (!FriendRequestProtocol.shouldAttachmentButtonBeEnabled(this, recipient)) {
|
||||
buttonToggle.display(sendButton);
|
||||
quickAttachmentToggle.hide();
|
||||
inlineAttachmentToggle.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputPanel.isRecordingInLockedMode()) {
|
||||
buttonToggle.display(sendButton);
|
||||
quickAttachmentToggle.show();
|
||||
@ -3106,7 +3082,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
||||
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
||||
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
if (publicChat != null) {
|
||||
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
||||
if (userCount == null) { userCount = 0; }
|
||||
@ -3192,19 +3168,5 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
updateSubtitleTextView();
|
||||
updateMessageStatusProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
|
||||
if (recipient.isGroupRecipient()) { return; }
|
||||
FriendRequestProtocol.acceptFriendRequest(this, recipient);
|
||||
updateInputPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
|
||||
if (recipient.isGroupRecipient()) { return; }
|
||||
FriendRequestProtocol.rejectFriendRequest(this, recipient);
|
||||
updateInputPanel();
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@ -108,8 +107,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
private MessageRecord recordToPulseHighlight;
|
||||
private String searchQuery;
|
||||
|
||||
public FriendRequestViewDelegate friendRequestViewDelegate; // Loki
|
||||
|
||||
protected static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public <V extends View & BindableConversationItem> ViewHolder(final @NonNull V itemView) {
|
||||
super(itemView);
|
||||
@ -202,11 +199,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getRecordForPositionOrThrow(adapterPosition + 1) : null;
|
||||
MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getRecordForPositionOrThrow(adapterPosition - 1) : null;
|
||||
|
||||
BindableConversationItem conversationItem = viewHolder.getView();
|
||||
if (conversationItem instanceof ConversationItem) {
|
||||
((ConversationItem)conversationItem).friendRequestViewDelegate = this.friendRequestViewDelegate;
|
||||
}
|
||||
conversationItem.bind(messageRecord,
|
||||
viewHolder.getView().bind(messageRecord,
|
||||
Optional.fromNullable(previousRecord),
|
||||
Optional.fromNullable(nextRecord),
|
||||
glideRequests,
|
||||
|
@ -79,7 +79,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
|
||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
@ -101,8 +100,8 @@ import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -151,7 +150,6 @@ public class ConversationFragment extends Fragment
|
||||
private View composeDivider;
|
||||
private View scrollToBottomButton;
|
||||
private TextView scrollDateHeader;
|
||||
public FriendRequestViewDelegate friendRequestViewDelegate; // Loki
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
@ -375,9 +373,7 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
|
||||
if (messageRecords.size() > 1) {
|
||||
// menu.findItem(R.id.menu_context_forward).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(false);
|
||||
// menu.findItem(R.id.menu_context_details).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_save_attachment).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_resend).setVisible(false);
|
||||
} else {
|
||||
@ -390,32 +386,29 @@ public class ConversationFragment extends Fragment
|
||||
((MediaMmsMessageRecord)messageRecord).containsMediaSlide() &&
|
||||
((MediaMmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() == null);
|
||||
|
||||
/*
|
||||
menu.findItem(R.id.menu_context_forward).setVisible(!actionMessage && !sharedContact);
|
||||
menu.findItem(R.id.menu_context_details).setVisible(!actionMessage);
|
||||
*/
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(!actionMessage &&
|
||||
!messageRecord.isPending() &&
|
||||
!messageRecord.isFailed() &&
|
||||
messageRecord.isSecure());
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_context_copy).setVisible(!actionMessage && hasText);
|
||||
|
||||
boolean isGroupChat = recipient.isGroupRecipient();
|
||||
|
||||
if (isGroupChat) {
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
boolean isPublicChat = publicChat != null;
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
boolean isPublicChat = (publicChat != null);
|
||||
int selectedMessageCount = messageRecords.size();
|
||||
boolean areAllSentByUser = true;
|
||||
for (MessageRecord message : messageRecords) {
|
||||
if (!message.isOutgoing()) { areAllSentByUser = false; }
|
||||
}
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(isPublicChat && selectedMessageCount == 1 && !areAllSentByUser);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(isPublicChat && selectedMessageCount == 1);
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1);
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||
boolean userCanModerate = isPublicChat && LokiPublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer());
|
||||
boolean isDeleteOptionVisible = isPublicChat && (areAllSentByUser || userCanModerate);
|
||||
boolean userCanModerate = isPublicChat && PublicChatAPI.Companion.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer());
|
||||
boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate);
|
||||
menu.findItem(R.id.menu_context_delete_message).setVisible(isDeleteOptionVisible);
|
||||
} else {
|
||||
menu.findItem(R.id.menu_context_copy_public_key).setVisible(false);
|
||||
@ -433,9 +426,7 @@ public class ConversationFragment extends Fragment
|
||||
|
||||
private MessageRecord getSelectedMessageRecord() {
|
||||
Set<MessageRecord> messageRecords = getListAdapter().getSelectedItems();
|
||||
|
||||
if (messageRecords.size() == 1) return messageRecords.iterator().next();
|
||||
else throw new AssertionError();
|
||||
return messageRecords.iterator().next();
|
||||
}
|
||||
|
||||
public void reload(Recipient recipient, long threadId) {
|
||||
@ -509,7 +500,7 @@ public class ConversationFragment extends Fragment
|
||||
builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount));
|
||||
builder.setCancelable(true);
|
||||
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId);
|
||||
|
||||
builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
@ -525,7 +516,7 @@ public class ConversationFragment extends Fragment
|
||||
ArrayList<Long> ignoredMessages = new ArrayList<>();
|
||||
ArrayList<Long> failedMessages = new ArrayList<>();
|
||||
boolean isSentByUser = true;
|
||||
LokiPublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getLokiPublicChatAPI();
|
||||
PublicChatAPI publicChatAPI = ApplicationContext.getInstance(getContext()).getPublicChatAPI();
|
||||
for (MessageRecord messageRecord : messageRecords) {
|
||||
isSentByUser = isSentByUser && messageRecord.isOutgoing();
|
||||
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id);
|
||||
@ -713,7 +704,6 @@ public class ConversationFragment extends Fragment
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
adapter.friendRequestViewDelegate = this.friendRequestViewDelegate;
|
||||
|
||||
if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) {
|
||||
adapter.setFooterView(topLoadMoreView);
|
||||
|
@ -86,10 +86,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
||||
import org.thoughtcrime.securesms.loki.views.FriendRequestView;
|
||||
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
|
||||
import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||
@ -112,8 +109,8 @@ import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -158,7 +155,6 @@ public class ConversationItem extends LinearLayout
|
||||
private ViewGroup contactPhotoHolder;
|
||||
private AlertView alertView;
|
||||
private ViewGroup container;
|
||||
private FriendRequestView friendRequestView;
|
||||
|
||||
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
|
||||
private Recipient conversationRecipient;
|
||||
@ -182,8 +178,6 @@ public class ConversationItem extends LinearLayout
|
||||
|
||||
private final Context context;
|
||||
|
||||
public FriendRequestViewDelegate friendRequestViewDelegate; // Loki
|
||||
|
||||
public ConversationItem(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@ -223,7 +217,6 @@ public class ConversationItem extends LinearLayout
|
||||
this.groupSenderHolder = findViewById(R.id.group_sender_holder);
|
||||
this.quoteView = findViewById(R.id.quote_view);
|
||||
this.container = findViewById(R.id.container);
|
||||
this.friendRequestView = findViewById(R.id.friend_request_view);
|
||||
|
||||
setOnClickListener(new ClickListener(null));
|
||||
|
||||
@ -269,7 +262,6 @@ public class ConversationItem extends LinearLayout
|
||||
setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setMessageSpacing(context, messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setFooter(messageRecord, nextMessageRecord, locale, groupThread);
|
||||
setFriendRequestView(messageRecord);
|
||||
adjustMarginsIfNeeded(messageRecord);
|
||||
}
|
||||
|
||||
@ -801,13 +793,14 @@ public class ConversationItem extends LinearLayout
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)bodyBubble.getLayoutParams();
|
||||
int groupThreadMargin = (int)((12 * getResources().getDisplayMetrics().density) + getResources().getDimension(R.dimen.small_profile_picture_size));
|
||||
int defaultMargin = 0;
|
||||
String threadName = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(messageRecord.getThreadId()).getName();
|
||||
Recipient r = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(messageRecord.getThreadId());
|
||||
String threadName = r != null ? r.getName() : "";
|
||||
boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates"));
|
||||
layoutParams.setMarginStart((groupThread && !isRSSFeed) ? groupThreadMargin : defaultMargin);
|
||||
bodyBubble.setLayoutParams(layoutParams);
|
||||
if (profilePictureView == null) return;
|
||||
profilePictureView.setHexEncodedPublicKey(recipient.getAddress().toString());
|
||||
profilePictureView.setAdditionalHexEncodedPublicKey(null);
|
||||
profilePictureView.setPublicKey(recipient.getAddress().toString());
|
||||
profilePictureView.setAdditionalPublicKey(null);
|
||||
profilePictureView.setRSSFeed(false);
|
||||
profilePictureView.setGlide(glideRequests);
|
||||
profilePictureView.update();
|
||||
@ -919,11 +912,6 @@ public class ConversationItem extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
private void setFriendRequestView(@NonNull MessageRecord record) {
|
||||
friendRequestView.setDelegate(friendRequestViewDelegate);
|
||||
friendRequestView.update(record);
|
||||
}
|
||||
|
||||
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
|
||||
if (hasSticker(messageRecord)) {
|
||||
return stickerFooter;
|
||||
@ -1000,9 +988,9 @@ public class ConversationItem extends LinearLayout
|
||||
profilePictureView.setVisibility(VISIBLE);
|
||||
int visibility = View.GONE;
|
||||
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId());
|
||||
PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId());
|
||||
if (publicChat != null) {
|
||||
boolean isModerator = LokiPublicChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer());
|
||||
boolean isModerator = PublicChatAPI.Companion.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer());
|
||||
visibility = isModerator ? View.VISIBLE : View.GONE;
|
||||
}
|
||||
|
||||
@ -1065,14 +1053,6 @@ public class ConversationItem extends LinearLayout
|
||||
int spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse);
|
||||
int spacingBottom = spacingTop;
|
||||
|
||||
boolean isOutgoingStack = current.isOutgoing() && previous.orNull() != null && previous.get().isOutgoing();
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
boolean isPreviousMessageFriendRequest = previous.orNull() != null && lokiMessageDatabase.isFriendRequest(previous.get().id);
|
||||
|
||||
if (isOutgoingStack && isPreviousMessageFriendRequest) {
|
||||
spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
|
||||
}
|
||||
|
||||
if (isStartOfMessageCluster(current, previous, isGroupThread)) {
|
||||
spacingTop = readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
|
||||
}
|
||||
|
@ -27,15 +27,14 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RecipientDatabase extends Database {
|
||||
|
||||
private static final String TAG = RecipientDatabase.class.getSimpleName();
|
||||
|
||||
static final String TABLE_NAME = "recipient_preferences";
|
||||
static final String TABLE_NAME = "recipient_preferences";
|
||||
private static final String ID = "_id";
|
||||
static final String ADDRESS = "recipient_ids";
|
||||
public static final String ADDRESS = "recipient_ids";
|
||||
private static final String BLOCK = "block";
|
||||
private static final String NOTIFICATION = "notification";
|
||||
private static final String VIBRATE = "vibrate";
|
||||
|
@ -885,7 +885,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
||||
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
|
||||
message.getSubscriptionId(), message.getExpiresIn(),
|
||||
System.currentTimeMillis(), 0, false, message.isFriendRequest);
|
||||
System.currentTimeMillis(), 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,15 +934,12 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument);
|
||||
Recipient recipient = Recipient.from(context, address, true);
|
||||
|
||||
// Loki - Check to see if this message was a friend request
|
||||
boolean isFriendRequest = DatabaseFactory.getLokiMessageDatabase(context).isFriendRequest(messageId);
|
||||
|
||||
return new SmsMessageRecord(messageId, body, recipient,
|
||||
recipient,
|
||||
addressDeviceId,
|
||||
dateSent, dateReceived, deliveryReceiptCount, type,
|
||||
threadId, status, mismatches, subscriptionId,
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified, isFriendRequest);
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified);
|
||||
}
|
||||
|
||||
private List<IdentityKeyMismatch> getMismatches(String document) {
|
||||
|
@ -12,6 +12,7 @@ import android.text.TextUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.DatabaseUpgradeActivity;
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
|
||||
@ -423,7 +424,7 @@ public class ClassicOpenHelper extends SQLiteOpenHelper {
|
||||
db.endTransaction();
|
||||
|
||||
// DecryptingQueue.schedulePendingDecrypts(context, masterSecret);
|
||||
MessageNotifier.updateNotification(context);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,7 +45,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@ -83,8 +83,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
private static final int lokiV7 = 28;
|
||||
private static final int lokiV8 = 29;
|
||||
private static final int lokiV9 = 30;
|
||||
private static final int lokiV10 = 31;
|
||||
private static final int lokiV11 = 32;
|
||||
|
||||
private static final int DATABASE_VERSION = lokiV9; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final int DATABASE_VERSION = lokiV11; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
@ -143,12 +145,14 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(LokiAPIDatabase.getCreateDeviceLinkCacheCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateUserCountCacheCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampCacheCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampCacheCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampCacheCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyDBCommand());
|
||||
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
|
||||
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
|
||||
db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand());
|
||||
db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
|
||||
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
|
||||
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
|
||||
db.execSQL(LokiThreadDatabase.getCreateFriendRequestTableCommand());
|
||||
db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand());
|
||||
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
|
||||
db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand());
|
||||
@ -545,7 +549,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
try (Cursor lokiPublicChatCursor = db.rawQuery("SELECT public_chat FROM loki_public_chat_database", null)) {
|
||||
while (lokiPublicChatCursor != null && lokiPublicChatCursor.moveToNext()) {
|
||||
String chatString = lokiPublicChatCursor.getString(0);
|
||||
LokiPublicChat publicChat = LokiPublicChat.fromJSON(chatString);
|
||||
PublicChat publicChat = PublicChat.fromJSON(chatString);
|
||||
if (publicChat != null) {
|
||||
byte[] groupId = publicChat.getId().getBytes();
|
||||
String oldId = GroupUtil.getEncodedId(groupId, false);
|
||||
@ -590,6 +594,15 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathCacheCommand());
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV10) {
|
||||
db.execSQL(LokiAPIDatabase.getCreateSessionRequestSentTimestampCacheCommand());
|
||||
db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampCacheCommand());
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV11) {
|
||||
db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyDBCommand());
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities;
|
||||
import org.thoughtcrime.securesms.util.AsyncLoader;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
|
@ -39,23 +39,6 @@ import network.loki.messenger.R;
|
||||
*/
|
||||
public class SmsMessageRecord extends MessageRecord {
|
||||
|
||||
// Loki
|
||||
private final boolean isFriendRequest;
|
||||
|
||||
public SmsMessageRecord(long id,
|
||||
String body, Recipient recipient,
|
||||
Recipient individualRecipient,
|
||||
int recipientDeviceId,
|
||||
long dateSent, long dateReceived,
|
||||
int deliveryReceiptCount,
|
||||
long type, long threadId,
|
||||
int status, List<IdentityKeyMismatch> mismatches,
|
||||
int subscriptionId, long expiresIn, long expireStarted,
|
||||
int readReceiptCount, boolean unidentified)
|
||||
{
|
||||
this(id, body, recipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, deliveryReceiptCount, type, threadId, status, mismatches, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, false);
|
||||
}
|
||||
|
||||
public SmsMessageRecord(long id,
|
||||
String body, Recipient recipient,
|
||||
Recipient individualRecipient,
|
||||
@ -65,22 +48,18 @@ public class SmsMessageRecord extends MessageRecord {
|
||||
long type, long threadId,
|
||||
int status, List<IdentityKeyMismatch> mismatches,
|
||||
int subscriptionId, long expiresIn, long expireStarted,
|
||||
int readReceiptCount, boolean unidentified, boolean isFriendRequest)
|
||||
int readReceiptCount, boolean unidentified)
|
||||
{
|
||||
super(id, body, recipient, individualRecipient, recipientDeviceId,
|
||||
dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
|
||||
mismatches, new LinkedList<>(), subscriptionId,
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified);
|
||||
this.isFriendRequest = isFriendRequest;
|
||||
}
|
||||
|
||||
public long getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
// Loki
|
||||
public boolean isFriendRequest() { return isFriendRequest; }
|
||||
|
||||
@Override
|
||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||
Recipient recipient = getRecipient();
|
||||
|
@ -45,7 +45,7 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
|
||||
@ -152,12 +152,11 @@ public class SignalCommunicationModule {
|
||||
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
|
||||
Optional.of(new MessageSenderEventListener(context)),
|
||||
TextSecurePreferences.getLocalNumber(context),
|
||||
TextSecurePreferences.getMasterHexEncodedPublicKey(context),
|
||||
DatabaseFactory.getLokiAPIDatabase(context),
|
||||
DatabaseFactory.getLokiThreadDatabase(context),
|
||||
DatabaseFactory.getLokiMessageDatabase(context),
|
||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
|
||||
new LokiSessionResetImplementation(context),
|
||||
new SessionResetImplementation(context),
|
||||
DatabaseFactory.getLokiUserDatabase(context),
|
||||
((ApplicationContext)context.getApplicationContext()).broadcaster);
|
||||
} else {
|
||||
|
@ -14,7 +14,7 @@ public class ChunkedImageUrl implements Key {
|
||||
public static final long SIZE_UNKNOWN = -1;
|
||||
|
||||
private final String url;
|
||||
private final long size;
|
||||
private final long size;
|
||||
|
||||
public ChunkedImageUrl(@NonNull String url) {
|
||||
this(url, SIZE_UNKNOWN);
|
||||
@ -22,7 +22,7 @@ public class ChunkedImageUrl implements Key {
|
||||
|
||||
public ChunkedImageUrl(@NonNull String url, long size) {
|
||||
this.url = url;
|
||||
this.size = size;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
@ -35,6 +35,7 @@ public class ChunkedImageUrl implements Key {
|
||||
|
||||
@Override
|
||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||
if (url == null) { return; }
|
||||
messageDigest.update(url.getBytes());
|
||||
messageDigest.update(Conversions.longToByteArray(size));
|
||||
}
|
||||
@ -45,12 +46,14 @@ public class ChunkedImageUrl implements Key {
|
||||
|
||||
ChunkedImageUrl that = (ChunkedImageUrl)other;
|
||||
|
||||
if (this.url == null || that.url == null) { return false; }
|
||||
|
||||
return this.url.equals(that.url) && this.size == that.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (url == null) { return 0; }
|
||||
return url.hashCode() ^ (int)size;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
@ -33,7 +32,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -131,10 +130,12 @@ public class GroupMessageProcessor {
|
||||
String id = GroupUtil.getEncodedId(group);
|
||||
|
||||
String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||
if (userMasterDevice == null) { userMasterDevice = TextSecurePreferences.getLocalNumber(context); }
|
||||
|
||||
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
|
||||
// Loki - Only update the group if the group admin sent the message
|
||||
String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
|
||||
if (masterDevice == null) { masterDevice = content.getSender(); }
|
||||
if (!groupRecord.getAdmins().contains(Address.fromSerialized(masterDevice))) {
|
||||
Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring.");
|
||||
return null;
|
||||
@ -212,6 +213,7 @@ public class GroupMessageProcessor {
|
||||
@NonNull GroupRecord record)
|
||||
{
|
||||
String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
|
||||
if (masterDevice == null) { masterDevice = content.getSender(); }
|
||||
if (record.getMembers().contains(Address.fromSerialized(masterDevice))) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
@ -234,6 +236,7 @@ public class GroupMessageProcessor {
|
||||
builder.setType(GroupContext.Type.QUIT);
|
||||
|
||||
String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
|
||||
if (masterDevice == null) { masterDevice = content.getSender(); }
|
||||
if (members.contains(Address.fromExternal(context, masterDevice))) {
|
||||
database.remove(id, Address.fromExternal(context, masterDevice));
|
||||
if (outgoing) database.setActive(id, false);
|
||||
@ -259,7 +262,7 @@ public class GroupMessageProcessor {
|
||||
try {
|
||||
if (outgoing) {
|
||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
|
||||
Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
|
||||
Recipient recipient = Recipient.from(context, address, false);
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, null, Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
@ -277,7 +280,7 @@ public class GroupMessageProcessor {
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
return insertResult.get().getThreadId();
|
||||
} else {
|
||||
return null;
|
||||
|
@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.jobs.SmsSentJob;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.UpdateApkJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -74,6 +75,7 @@ public class WorkManagerFactoryMappings {
|
||||
put(PushMediaSendJob.class.getName(), PushMediaSendJob.KEY);
|
||||
put(PushNotificationReceiveJob.class.getName(), PushNotificationReceiveJob.KEY);
|
||||
put(PushTextSendJob.class.getName(), PushTextSendJob.KEY);
|
||||
put(PushNullMessageSendJob.class.getName(), PushNullMessageSendJob.KEY);
|
||||
put(RefreshAttributesJob.class.getName(), RefreshAttributesJob.KEY);
|
||||
put(RefreshPreKeysJob.class.getName(), RefreshPreKeysJob.KEY);
|
||||
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
|
||||
|
@ -5,6 +5,7 @@ import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
@ -107,7 +108,7 @@ public class AttachmentDownloadJob extends BaseJob implements InjectableType {
|
||||
@Override
|
||||
public void onRun() throws IOException {
|
||||
doWork();
|
||||
MessageNotifier.updateNotification(context, 0);
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, 0);
|
||||
}
|
||||
|
||||
public void doWork() throws IOException {
|
||||
|
@ -27,7 +27,6 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -53,7 +52,7 @@ public class AttachmentUploadJob extends BaseJob implements InjectableType {
|
||||
this(new Job.Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(3)
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
attachmentId, destination);
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushSessionRequestMessageSendJob;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -50,6 +51,7 @@ public final class JobManagerFactories {
|
||||
put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory());
|
||||
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
||||
put(PushTextSendJob.KEY, new PushTextSendJob.Factory());
|
||||
put(PushNullMessageSendJob.KEY, new PushNullMessageSendJob.Factory());
|
||||
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
||||
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
|
||||
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
|
||||
@ -70,7 +72,7 @@ public final class JobManagerFactories {
|
||||
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
|
||||
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
||||
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
||||
put(PushEphemeralMessageSendJob.KEY, new PushEphemeralMessageSendJob.Factory());
|
||||
put(PushSessionRequestMessageSendJob.KEY, new PushSessionRequestMessageSendJob.Factory());
|
||||
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
|
||||
}};
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import com.google.android.mms.pdu_alt.PduBody;
|
||||
import com.google.android.mms.pdu_alt.PduPart;
|
||||
import com.google.android.mms.pdu_alt.RetrieveConf;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
@ -57,6 +58,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||
private long messageId;
|
||||
private long threadId;
|
||||
private boolean automatic;
|
||||
private MessageNotifier messageNotifier;
|
||||
|
||||
public MmsDownloadJob(long messageId, long threadId, boolean automatic) {
|
||||
this(new Job.Parameters.Builder()
|
||||
@ -75,6 +77,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||
this.messageId = messageId;
|
||||
this.threadId = threadId;
|
||||
this.automatic = automatic;
|
||||
this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -94,7 +97,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||
public void onAdded() {
|
||||
if (automatic && KeyCachingService.isLocked(context)) {
|
||||
DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId);
|
||||
MessageNotifier.updateNotification(context);
|
||||
messageNotifier.updateNotification(context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +180,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||
|
||||
if (automatic) {
|
||||
database.markIncomingNotificationReceived(threadId);
|
||||
MessageNotifier.updateNotification(context, threadId);
|
||||
messageNotifier.updateNotification(context, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,7 +255,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
database.delete(messageId);
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +267,7 @@ public class MmsDownloadJob extends BaseJob {
|
||||
|
||||
if (automatic) {
|
||||
db.markIncomingNotificationReceived(threadId);
|
||||
MessageNotifier.updateNotification(context, threadId);
|
||||
messageNotifier.updateNotification(context, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import com.google.android.mms.pdu_alt.SendReq;
|
||||
import com.google.android.mms.smil.SmilHelper;
|
||||
import com.klinker.android.send_message.Utils;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -304,7 +305,7 @@ public class MmsSendJob extends SendJob {
|
||||
Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
||||
|
||||
if (recipient != null) {
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
|
||||
}
|
||||
|
||||
if (address == null) generateFullContactUpdate();
|
||||
else if (SyncMessagesProtocol.shouldSyncContact(context, Address.fromSerialized(address))) generateSingleContactUpdate(Address.fromSerialized(address));
|
||||
else if (SyncMessagesProtocol.shouldSyncContact(context, address)) generateSingleContactUpdate(Address.fromSerialized(address));
|
||||
}
|
||||
|
||||
private void generateSingleContactUpdate(@NonNull Address address)
|
||||
@ -139,8 +139,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
|
||||
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
|
||||
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
|
||||
|
||||
// Loki - Only sync contacts we are friends with
|
||||
if (SyncMessagesProtocol.shouldSyncContact(context, address)) {
|
||||
if (SyncMessagesProtocol.shouldSyncContact(context, address.serialize())) {
|
||||
out.write(new DeviceContact(address.toPhoneString(),
|
||||
Optional.fromNullable(recipient.getName()),
|
||||
getAvatar(recipient.getContactUri()),
|
||||
|
@ -48,7 +48,6 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
@ -66,15 +65,11 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
|
||||
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol;
|
||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
|
||||
import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities;
|
||||
@ -102,7 +97,7 @@ import org.thoughtcrime.securesms.util.Hex;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol;
|
||||
import org.whispersystems.libsignal.loki.SessionResetProtocol;
|
||||
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
@ -127,13 +122,11 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI;
|
||||
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
|
||||
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -145,8 +138,6 @@ import javax.inject.Inject;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import static org.thoughtcrime.securesms.loki.utilities.RecipientUtilitiesKt.recipient;
|
||||
|
||||
public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
public static final String KEY = "PushDecryptJob";
|
||||
@ -159,8 +150,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
private long messageId;
|
||||
private long smsMessageId;
|
||||
|
||||
private MessageNotifier messageNotifier;
|
||||
|
||||
@Inject SignalServiceMessageSender messageSender;
|
||||
private Address author;
|
||||
|
||||
public PushDecryptJob(Context context) {
|
||||
this(context, -1);
|
||||
@ -178,6 +170,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
pushMessageId,
|
||||
smsMessageId);
|
||||
setContext(context);
|
||||
this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier;
|
||||
}
|
||||
|
||||
private PushDecryptJob(@NonNull Job.Parameters parameters, long pushMessageId, long smsMessageId) {
|
||||
@ -223,9 +216,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
|
||||
}
|
||||
public void onCanceled() { }
|
||||
|
||||
public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) {
|
||||
synchronized (PushReceivedJob.RECEIVE_LOCK) {
|
||||
@ -260,32 +251,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional<Long> smsMessageId, boolean isPushNotification) {
|
||||
try {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||
LokiSessionResetProtocol lokiSessionResetProtocol = new LokiSessionResetImplementation(context);
|
||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiSessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||
SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context);
|
||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, sessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
|
||||
|
||||
SignalServiceContent content = cipher.decrypt(envelope);
|
||||
|
||||
// Loki - Ignore any friend requests from before restoration
|
||||
if (FriendRequestProtocol.isFriendRequestFromBeforeRestoration(context, content)) {
|
||||
Log.d("Loki", "Ignoring friend request from before restoration.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldIgnore(content)) {
|
||||
Log.i(TAG, "Ignoring message.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Loki - Handle pre key bundle message if needed
|
||||
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content);
|
||||
|
||||
// Loki - Handle session request if needed
|
||||
if (SessionManagementProtocol.handleSessionRequestIfNeeded(context, content)) { return; } // Don't process the message any further
|
||||
|
||||
// Loki - Handle profile update if needed
|
||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||
|
||||
if (content.getDeviceLink().isPresent()) {
|
||||
@ -294,15 +274,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
||||
|
||||
// Loki - Handle unlinking request if needed
|
||||
if (message.isUnlinkingRequest()) {
|
||||
if (message.isDeviceUnlinkingRequest()) {
|
||||
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
|
||||
} else {
|
||||
// Loki - Don't process session restoration requests any further
|
||||
if (message.isSessionRestorationRequest()) { return; }
|
||||
|
||||
// Loki - Handle friend request acceptance if needed
|
||||
FriendRequestProtocol.handleFriendRequestAcceptanceIfNeeded(context, content.getSender(), content);
|
||||
|
||||
if (message.isEndSession()) {
|
||||
handleEndSessionMessage(content, smsMessageId);
|
||||
@ -312,52 +286,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
handleExpirationUpdate(content, message, smsMessageId);
|
||||
} else if (isMediaMessage) {
|
||||
handleMediaMessage(content, message, smsMessageId, Optional.absent());
|
||||
|
||||
// Loki - This is needed for compatibility with refactored desktop clients
|
||||
if (!message.isGroupMessage()) {
|
||||
Recipient recipient = recipient(context, content.getSender());
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context);
|
||||
LokiThreadFriendRequestStatus threadFriendRequestStatus = threadDB.getFriendRequestStatus(threadID);
|
||||
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE || threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
||||
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED);
|
||||
} else if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
|
||||
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
||||
EphemeralMessage ephemeralMessage = EphemeralMessage.create(content.getSender());
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushEphemeralMessageSendJob(ephemeralMessage));
|
||||
SyncMessagesProtocol.syncContact(context, Address.fromSerialized(content.getSender()));
|
||||
}
|
||||
|
||||
// Loki - Handle friend request message if needed
|
||||
FriendRequestProtocol.handleFriendRequestMessageIfNeeded(context, content.getSender(), content);
|
||||
}
|
||||
} else if (message.getBody().isPresent()) {
|
||||
handleTextMessage(content, message, smsMessageId, Optional.absent());
|
||||
|
||||
// Loki - This is needed for compatibility with refactored desktop clients
|
||||
if (!message.isGroupMessage()) {
|
||||
Recipient recipient = recipient(context, content.getSender());
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context);
|
||||
LokiThreadFriendRequestStatus threadFriendRequestStatus = threadDB.getFriendRequestStatus(threadID);
|
||||
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE || threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
||||
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED);
|
||||
} else if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
|
||||
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
||||
EphemeralMessage ephemeralMessage = EphemeralMessage.create(content.getSender());
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushEphemeralMessageSendJob(ephemeralMessage));
|
||||
SyncMessagesProtocol.syncContact(context, Address.fromSerialized(content.getSender()));
|
||||
}
|
||||
|
||||
// Loki - Handle friend request message if needed
|
||||
FriendRequestProtocol.handleFriendRequestMessageIfNeeded(context, content.getSender(), content);
|
||||
}
|
||||
} else {
|
||||
// Loki - This is needed for compatibility with refactored desktop clients
|
||||
if (envelope.isFriendRequest()) {
|
||||
EphemeralMessage ephemeralMessage = EphemeralMessage.create(content.getSender());
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushEphemeralMessageSendJob(ephemeralMessage));
|
||||
}
|
||||
}
|
||||
|
||||
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) {
|
||||
@ -365,7 +295,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
|
||||
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
|
||||
handleProfileKey(content, message);
|
||||
SessionMetaProtocol.handleProfileKeyUpdate(context, content);
|
||||
}
|
||||
|
||||
if (content.isNeedsReceipt()) {
|
||||
@ -385,6 +315,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get());
|
||||
else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get());
|
||||
else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get());
|
||||
else if (syncMessage.getBlockedList().isPresent()) SyncMessagesProtocol.handleBlockedContactsSyncMessage(context, content, syncMessage.getBlockedList().get());
|
||||
else Log.w(TAG, "Contains no known sync types...");
|
||||
} else if (content.getCallMessage().isPresent()) {
|
||||
Log.i(TAG, "Got call message...");
|
||||
@ -546,7 +477,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
if (threadId != null) {
|
||||
SessionManagementProtocol.handleEndSessionMessageIfNeeded(context, content);
|
||||
MessageNotifier.updateNotification(context, threadId);
|
||||
messageNotifier.updateNotification(context, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -700,19 +631,17 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||
}
|
||||
|
||||
// Loki - Handle profile key update if needed
|
||||
handleProfileKey(content, message.getMessage());
|
||||
SessionMetaProtocol.handleProfileKeyUpdate(context, content);
|
||||
}
|
||||
|
||||
// Loki - Update profile if needed
|
||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||
|
||||
if (threadId != null) {
|
||||
DatabaseFactory.getThreadDatabase(context).setRead(threadId, true);
|
||||
MessageNotifier.updateNotification(context);
|
||||
messageNotifier.updateNotification(context);
|
||||
}
|
||||
|
||||
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
|
||||
messageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
|
||||
} catch (MmsException e) {
|
||||
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
|
||||
}
|
||||
@ -775,9 +704,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
}
|
||||
|
||||
MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
|
||||
MessageNotifier.cancelDelayedNotifications();
|
||||
MessageNotifier.updateNotification(context);
|
||||
messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
|
||||
messageNotifier.cancelDelayedNotifications();
|
||||
messageNotifier.updateNotification(context);
|
||||
}
|
||||
|
||||
public void handleMediaMessage(@NonNull SignalServiceContent content,
|
||||
@ -809,7 +738,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
database.beginTransaction();
|
||||
|
||||
// Loki - Ignore message if it has no body and no attachments
|
||||
// Ignore message if it has no body and no attachments
|
||||
if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@ -843,23 +772,31 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
|
||||
// Loki - Store message open group server ID if needed
|
||||
if (insertResult.isPresent() && messageServerIDOrNull.isPresent()) {
|
||||
long messageID = insertResult.get().getMessageId();
|
||||
long messageServerID = messageServerIDOrNull.get();
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
lokiMessageDatabase.setServerID(messageID, messageServerID);
|
||||
}
|
||||
|
||||
// Loki - Update mapping of message ID to original thread ID
|
||||
if (insertResult.isPresent()) {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
|
||||
lokiMessageDatabase.setOriginalThreadID(insertResult.get().getMessageId(), originalThreadId);
|
||||
InsertResult result = insertResult.get();
|
||||
|
||||
// Loki - Cache the user hex encoded public key (for mentions)
|
||||
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context);
|
||||
MentionsManager.shared.cache(content.getSender(), result.getThreadId());
|
||||
|
||||
// Loki - Store message open group server ID if needed
|
||||
if (messageServerIDOrNull.isPresent()) {
|
||||
long messageID = result.getMessageId();
|
||||
long messageServerID = messageServerIDOrNull.get();
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
lokiMessageDatabase.setServerID(messageID, messageServerID);
|
||||
}
|
||||
|
||||
// Loki - Update mapping of message ID to original thread ID
|
||||
if (result.getMessageId() > -1) {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
|
||||
lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1015,7 +952,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get());
|
||||
|
||||
if (threadId != null) {
|
||||
MessageNotifier.updateNotification(context, threadId);
|
||||
messageNotifier.updateNotification(context, threadId);
|
||||
}
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
@ -1023,17 +960,17 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
// Loki - Cache the user hex encoded public key (for mentions)
|
||||
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context);
|
||||
MentionsManager.shared.cache(textMessage.getSender().serialize(), result.getThreadId());
|
||||
MentionsManager.shared.cache(content.getSender(), result.getThreadId());
|
||||
|
||||
// Loki - Store message server ID
|
||||
if (insertResult.isPresent() && messageServerIDOrNull.isPresent()) {
|
||||
long messageID = insertResult.get().getMessageId();
|
||||
// Loki - Store message open group server ID if needed
|
||||
if (messageServerIDOrNull.isPresent()) {
|
||||
long messageID = result.getMessageId();
|
||||
long messageServerID = messageServerIDOrNull.get();
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
lokiMessageDatabase.setServerID(messageID, messageServerID);
|
||||
}
|
||||
|
||||
// Loki - Update mapping of message to original thread ID
|
||||
// Loki - Update mapping of message ID to original thread ID
|
||||
if (result.getMessageId() > -1) {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
@ -1116,7 +1053,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else {
|
||||
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
|
||||
@ -1133,7 +1070,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else {
|
||||
smsDatabase.markAsDecryptFailed(smsMessageId.get());
|
||||
@ -1151,7 +1088,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else {
|
||||
smsDatabase.markAsNoSession(smsMessageId.get());
|
||||
@ -1169,7 +1106,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else {
|
||||
smsDatabase.markAsLegacyVersion(smsMessageId.get());
|
||||
@ -1192,28 +1129,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
// }
|
||||
}
|
||||
|
||||
private void handleProfileKey(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message)
|
||||
{
|
||||
if (!message.getProfileKey().isPresent()) { return; }
|
||||
|
||||
/*
|
||||
If we get a profile key then we don't need to map it to the primary device.
|
||||
For now a profile key is mapped one-to-one to avoid secondary devices setting the incorrect avatar for a primary device.
|
||||
*/
|
||||
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
||||
|
||||
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
|
||||
database.setProfileKey(recipient, message.getProfileKey().get());
|
||||
database.setUnidentifiedAccessMode(recipient, RecipientDatabase.UnidentifiedAccessMode.UNKNOWN);
|
||||
String url = content.senderProfilePictureURL.or("");
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(recipient, url));
|
||||
|
||||
SessionMetaProtocol.handleProfileKeyUpdateIfNeeded(context, content);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceDataMessage message)
|
||||
{
|
||||
@ -1441,14 +1356,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
} else {
|
||||
String publicKey = message.getDestination().get();
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||
Set<String> allUserDevices = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
||||
Set<String> allUserDevices = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
||||
if (allUserDevices.contains(publicKey)) {
|
||||
return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
|
||||
} else {
|
||||
try {
|
||||
// TODO: Burn this with fire when we can
|
||||
PromiseUtilities.timeout(LokiFileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
|
||||
String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
|
||||
PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
|
||||
String masterPublicKey = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
|
||||
if (masterPublicKey == null) {
|
||||
masterPublicKey = publicKey;
|
||||
}
|
||||
@ -1473,14 +1388,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
return Recipient.from(context, Address.fromSerialized(publicKey), false);
|
||||
} else {
|
||||
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||
Set<String> allUserDevices = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
||||
Set<String> allUserDevices = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
||||
if (allUserDevices.contains(publicKey)) {
|
||||
return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
|
||||
} else {
|
||||
try {
|
||||
// TODO: Burn this with fire when we can
|
||||
PromiseUtilities.timeout(LokiFileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
|
||||
String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
|
||||
PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
|
||||
String masterPublicKey = org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
|
||||
if (masterPublicKey == null) {
|
||||
masterPublicKey = publicKey;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.WorkerThread;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
@ -31,7 +30,6 @@ import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
@ -44,7 +42,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
||||
import org.whispersystems.signalservice.loki.api.SnodeAPI;
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
@ -64,46 +62,34 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id";
|
||||
private static final String KEY_MESSAGE_ID = "message_id";
|
||||
private static final String KEY_DESTINATION = "destination";
|
||||
private static final String KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE = "is_friend_request";
|
||||
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
|
||||
|
||||
@Inject SignalServiceMessageSender messageSender;
|
||||
|
||||
private long messageId; // The message ID
|
||||
private long templateMessageId; // The message ID of the message to template this send job from
|
||||
|
||||
// Loki - Multi device
|
||||
private Address destination; // Used to check whether this is another device we're sending to
|
||||
private boolean isLokiPreKeyBundleMessage; // Whether this is a friend request / session request / device link message
|
||||
private String customFriendRequestMessage; // If this isn't set then we use the message body
|
||||
private long messageId;
|
||||
private long templateMessageId;
|
||||
private Address destination;
|
||||
|
||||
public PushMediaSendJob(long messageId, Address destination) {
|
||||
this(messageId, messageId, destination);
|
||||
}
|
||||
|
||||
public PushMediaSendJob(long templateMessageId, long messageId, Address destination) {
|
||||
this(templateMessageId, messageId, destination, false, null);
|
||||
this(constructParameters(destination), templateMessageId, messageId, destination);
|
||||
}
|
||||
|
||||
public PushMediaSendJob(long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
|
||||
this(constructParameters(destination), templateMessageId, messageId, destination, isLokiPreKeyBundleMessage, customFriendRequestMessage);
|
||||
}
|
||||
|
||||
private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
|
||||
private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) {
|
||||
super(parameters);
|
||||
this.templateMessageId = templateMessageId;
|
||||
this.messageId = messageId;
|
||||
this.destination = destination;
|
||||
this.isLokiPreKeyBundleMessage = isLokiPreKeyBundleMessage;
|
||||
this.customFriendRequestMessage = customFriendRequestMessage;
|
||||
}
|
||||
|
||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) {
|
||||
enqueue(context, jobManager, messageId, messageId, destination, false, null);
|
||||
enqueue(context, jobManager, messageId, messageId, destination);
|
||||
}
|
||||
|
||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, Boolean isFriendRequest, @Nullable String customFriendRequestMessage) {
|
||||
enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage)));
|
||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination) {
|
||||
enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination)));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@ -144,14 +130,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
Data.Builder builder = new Data.Builder()
|
||||
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
||||
.putLong(KEY_MESSAGE_ID, messageId)
|
||||
.putString(KEY_DESTINATION, destination.serialize())
|
||||
.putBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE, isLokiPreKeyBundleMessage);
|
||||
|
||||
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
|
||||
return builder.build();
|
||||
return new Data.Builder()
|
||||
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
||||
.putLong(KEY_MESSAGE_ID, messageId)
|
||||
.putString(KEY_DESTINATION, destination.serialize()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -231,7 +213,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
|
||||
database.markAsSentFailed(messageId);
|
||||
}
|
||||
} catch (LokiAPI.Error e) {
|
||||
} catch (SnodeAPI.Error e) {
|
||||
Log.d("Loki", "Couldn't send message due to error: " + e.getDescription());
|
||||
if (messageId >= 0) {
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
@ -257,7 +239,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
private boolean deliver(OutgoingMediaMessage message)
|
||||
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
|
||||
UndeliverableMessageException, LokiAPI.Error
|
||||
UndeliverableMessageException, SnodeAPI.Error
|
||||
{
|
||||
try {
|
||||
Recipient recipient = Recipient.from(context, destination, false);
|
||||
@ -270,17 +252,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
List<SharedContact> sharedContacts = getSharedContactsFor(message);
|
||||
List<Preview> previews = getPreviewsFor(message);
|
||||
|
||||
// Loki - Include a pre key bundle if needed
|
||||
PreKeyBundle preKeyBundle;
|
||||
if (isLokiPreKeyBundleMessage) {
|
||||
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.getNumber());
|
||||
} else {
|
||||
preKeyBundle = null;
|
||||
}
|
||||
|
||||
String body = (isLokiPreKeyBundleMessage && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
|
||||
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withBody(body)
|
||||
.withBody(message.getBody())
|
||||
.withAttachments(serviceAttachments)
|
||||
.withTimestamp(message.getSentTimeMillis())
|
||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||
@ -290,8 +263,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
.withSharedContacts(sharedContacts)
|
||||
.withPreviews(previews)
|
||||
.asExpirationUpdate(message.isExpirationUpdate())
|
||||
.withPreKeyBundle(preKeyBundle)
|
||||
.asFriendRequest(isLokiPreKeyBundleMessage)
|
||||
.build();
|
||||
|
||||
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
|
||||
@ -327,9 +298,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
|
||||
long messageID = data.getLong(KEY_MESSAGE_ID);
|
||||
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
||||
boolean isLokiPreKeyBundleMessage = data.getBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE);
|
||||
String customFRMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
|
||||
return new PushMediaSendJob(parameters, templateMessageID, messageID, destination, isLokiPreKeyBundleMessage, customFRMessage);
|
||||
return new PushMediaSendJob(parameters, templateMessageID, messageID, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ public abstract class PushReceivedJob extends BaseJob {
|
||||
|
||||
if (envelope.isReceipt()) {
|
||||
handleReceipt(envelope);
|
||||
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender() || envelope.isFriendRequest()) {
|
||||
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender() || envelope.isFallbackMessage()) {
|
||||
handleMessage(envelope, isPushNotification);
|
||||
} else {
|
||||
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
|
||||
|
@ -182,7 +182,7 @@ public abstract class PushSendJob extends SendJob {
|
||||
Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
||||
|
||||
if (threadId != -1 && recipient != null) {
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
@ -32,7 +31,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
||||
import org.whispersystems.signalservice.loki.api.SnodeAPI;
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -48,50 +47,34 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id";
|
||||
private static final String KEY_MESSAGE_ID = "message_id";
|
||||
private static final String KEY_DESTINATION = "destination";
|
||||
private static final String KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE = "is_friend_request";
|
||||
private static final String KEY_CUSTOM_FR_MESSAGE = "custom_friend_request_message";
|
||||
|
||||
@Inject SignalServiceMessageSender messageSender;
|
||||
|
||||
private long messageId; // The message ID
|
||||
private long templateMessageId; // The message ID of the message to template this send job from
|
||||
|
||||
// Loki - Multi device
|
||||
private Address destination; // Used to check whether this is another device we're sending to
|
||||
private boolean isLokiPreKeyBundleMessage; // Whether this is a friend request / session request / device link message
|
||||
private String customFriendRequestMessage; // If this isn't set then we use the message body
|
||||
private long messageId;
|
||||
private long templateMessageId;
|
||||
private Address destination;
|
||||
|
||||
public PushTextSendJob(long messageId, Address destination) {
|
||||
this(messageId, messageId, destination);
|
||||
}
|
||||
|
||||
public PushTextSendJob(long templateMessageId, long messageId, Address destination) {
|
||||
this(templateMessageId, messageId, destination, false, null);
|
||||
this(constructParameters(destination), templateMessageId, messageId, destination);
|
||||
}
|
||||
|
||||
public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
|
||||
this(constructParameters(destination), templateMessageId, messageId, destination, isLokiPreKeyBundleMessage, customFriendRequestMessage);
|
||||
}
|
||||
|
||||
private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
|
||||
private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) {
|
||||
super(parameters);
|
||||
this.templateMessageId = templateMessageId;
|
||||
this.messageId = messageId;
|
||||
this.destination = destination;
|
||||
this.isLokiPreKeyBundleMessage = isLokiPreKeyBundleMessage;
|
||||
this.customFriendRequestMessage = customFriendRequestMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
Data.Builder builder = new Data.Builder()
|
||||
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
||||
.putLong(KEY_MESSAGE_ID, messageId)
|
||||
.putString(KEY_DESTINATION, destination.serialize())
|
||||
.putBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE, isLokiPreKeyBundleMessage);
|
||||
|
||||
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
|
||||
return builder.build();
|
||||
return new Data.Builder()
|
||||
.putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
|
||||
.putLong(KEY_MESSAGE_ID, messageId)
|
||||
.putString(KEY_DESTINATION, destination.serialize()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -164,7 +147,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
warn(TAG, "Couldn't send message due to error: ", e);
|
||||
if (messageId >= 0) {
|
||||
database.markAsPendingInsecureSmsFallback(record.getId());
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
}
|
||||
} catch (UntrustedIdentityException e) {
|
||||
warn(TAG, "Couldn't send message due to error: ", e);
|
||||
@ -173,7 +156,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
database.markAsSentFailed(record.getId());
|
||||
database.markAsPush(record.getId());
|
||||
}
|
||||
} catch (LokiAPI.Error e) {
|
||||
} catch (SnodeAPI.Error e) {
|
||||
Log.d("Loki", "Couldn't send message due to error: " + e.getDescription());
|
||||
if (messageId >= 0) {
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||
@ -198,13 +181,13 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
||||
|
||||
if (threadId != -1 && recipient != null) {
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deliver(SmsMessageRecord message)
|
||||
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, LokiAPI.Error
|
||||
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, SnodeAPI.Error
|
||||
{
|
||||
try {
|
||||
Recipient recipient = Recipient.from(context, destination, false);
|
||||
@ -214,23 +197,18 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
||||
|
||||
// Loki - Include a pre key bundle if needed
|
||||
PreKeyBundle preKeyBundle;
|
||||
if (isLokiPreKeyBundleMessage || message.isEndSession()) {
|
||||
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.getNumber());
|
||||
} else {
|
||||
preKeyBundle = null;
|
||||
PreKeyBundle preKeyBundle = null;
|
||||
if (message.isEndSession()) {
|
||||
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
|
||||
}
|
||||
|
||||
String body = (isLokiPreKeyBundleMessage && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
|
||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(message.getDateSent())
|
||||
.withBody(body)
|
||||
.withBody(message.getBody())
|
||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||
.withProfileKey(profileKey.orNull())
|
||||
.asEndSessionMessage(message.isEndSession())
|
||||
.asFriendRequest(isLokiPreKeyBundleMessage)
|
||||
.withPreKeyBundle(preKeyBundle)
|
||||
.asEndSessionMessage(message.isEndSession())
|
||||
.build();
|
||||
|
||||
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
|
||||
@ -263,9 +241,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
|
||||
long messageID = data.getLong(KEY_MESSAGE_ID);
|
||||
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
||||
boolean isLokiPreKeyBundleMessage = data.getBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE);
|
||||
String customFRMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
|
||||
return new PushTextSendJob(parameters, templateMessageID, messageID, destination, isLokiPreKeyBundleMessage, customFRMessage);
|
||||
return new PushTextSendJob(parameters, templateMessageID, messageID, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ public class RotateSignedPreKeyJob extends BaseJob implements InjectableType {
|
||||
public void onRun() throws Exception {
|
||||
Log.i(TAG, "Rotating signed prekey...");
|
||||
|
||||
if (!IdentityKeyUtil.hasIdentityKey(context)) { return; }
|
||||
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
|
||||
|
||||
|
@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||
@ -79,7 +80,7 @@ public class SmsReceiveJob extends BaseJob {
|
||||
Optional<InsertResult> insertResult = storeMessage(message.get());
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||
}
|
||||
} else if (message.isPresent()) {
|
||||
Log.w(TAG, "*** Received blocked SMS, ignoring...");
|
||||
|
@ -9,6 +9,7 @@ import android.support.annotation.NonNull;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.telephony.SmsManager;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
||||
@ -88,7 +89,7 @@ public class SmsSendJob extends SendJob {
|
||||
} catch (UndeliverableMessageException ude) {
|
||||
warn(TAG, ude);
|
||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId());
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +107,7 @@ public class SmsSendJob extends SendJob {
|
||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||
|
||||
if (threadId != -1 && recipient != null) {
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ public class SmsSentJob extends BaseJob {
|
||||
break;
|
||||
default:
|
||||
database.markAsSentFailed(messageId);
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
}
|
||||
} catch (NoSuchMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
|
@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.net.CallRequestController;
|
||||
import org.thoughtcrime.securesms.net.CompositeRequestController;
|
||||
import org.thoughtcrime.securesms.net.ContentProxySafetyInterceptor;
|
||||
import org.thoughtcrime.securesms.net.ContentProxySelector;
|
||||
import org.thoughtcrime.securesms.net.RequestController;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.stickers.StickerRemoteUri;
|
||||
@ -63,7 +62,7 @@ public class LinkPreviewRepository implements InjectableType {
|
||||
|
||||
public LinkPreviewRepository(@NonNull Context context) {
|
||||
this.client = new OkHttpClient.Builder()
|
||||
.proxySelector(new ContentProxySelector())
|
||||
// .proxySelector(new ContentProxySelector()) // Loki: Signal's proxy appears to have been banned by YouTube
|
||||
.addNetworkInterceptor(new ContentProxySafetyInterceptor())
|
||||
.cache(null)
|
||||
.build();
|
||||
|
@ -7,9 +7,12 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentPagerAdapter
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.activity_create_private_chat.*
|
||||
import kotlinx.android.synthetic.main.fragment_enter_public_key.*
|
||||
@ -25,6 +28,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
|
||||
|
||||
|
||||
class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||
private val adapter = CreatePrivateChatActivityAdapter(this)
|
||||
|
||||
@ -111,7 +115,18 @@ class EnterPublicKeyFragment : Fragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
publicKeyTextView.imeOptions = publicKeyTextView.imeOptions or 16777216 // Always use incognito keyboard
|
||||
publicKeyEditText.imeOptions = EditorInfo.IME_ACTION_DONE or 16777216 // Always use incognito keyboard
|
||||
publicKeyEditText.setRawInputType(InputType.TYPE_CLASS_TEXT)
|
||||
publicKeyEditText.setOnEditorActionListener { v, actionID, _ ->
|
||||
if (actionID == EditorInfo.IME_ACTION_DONE) {
|
||||
val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(v.windowToken, 0)
|
||||
createPrivateChatIfPossible()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
publicKeyTextView.text = hexEncodedPublicKey
|
||||
copyButton.setOnClickListener { copyPublicKey() }
|
||||
shareButton.setOnClickListener { sharePublicKey() }
|
||||
|
@ -41,9 +41,6 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
||||
if (displayName.isEmpty()) {
|
||||
return Toast.makeText(this, R.string.activity_display_name_display_name_missing_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
if (!displayName.matches(Regex("[a-zA-Z0-9_]+"))) {
|
||||
return Toast.makeText(this, R.string.activity_display_name_display_name_invalid_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) {
|
||||
return Toast.makeText(this, R.string.activity_display_name_display_name_too_long_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
@ -1,21 +1,20 @@
|
||||
package org.thoughtcrime.securesms.loki.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.arch.lifecycle.Observer
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.database.Cursor
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.support.v4.app.LoaderManager
|
||||
import android.support.v4.content.Loader
|
||||
import android.support.v4.content.LocalBroadcastManager
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.support.v7.widget.helper.ItemTouchHelper
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
@ -31,35 +30,36 @@ import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.loki.dialogs.PNModeBottomSheet
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob
|
||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
|
||||
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
|
||||
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
import org.thoughtcrime.securesms.loki.views.ConversationView
|
||||
import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate
|
||||
import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
|
||||
import kotlin.math.abs
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
|
||||
|
||||
class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate {
|
||||
private lateinit var glide: GlideRequests
|
||||
private var broadcastReceiver: BroadcastReceiver? = null
|
||||
|
||||
private val hexEncodedPublicKey: String
|
||||
private val publicKey: String
|
||||
get() {
|
||||
val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this)
|
||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
return masterHexEncodedPublicKey ?: userHexEncodedPublicKey
|
||||
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
return masterPublicKey ?: userPublicKey
|
||||
}
|
||||
|
||||
// region Lifecycle
|
||||
@ -77,7 +77,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
val threadID = archivedConversations.getLong(archivedConversations.getColumnIndex(ThreadDatabase.ID))
|
||||
AsyncTask.execute {
|
||||
threadDatabase.deleteConversation(threadID)
|
||||
MessageNotifier.updateNotification(this)
|
||||
(applicationContext as ApplicationContext).messageNotifier.updateNotification(this)
|
||||
}
|
||||
}
|
||||
deleteThreadAtCurrentPosition()
|
||||
@ -95,7 +95,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
glide = GlideApp.with(this)
|
||||
// Set up toolbar buttons
|
||||
profileButton.glide = glide
|
||||
profileButton.hexEncodedPublicKey = hexEncodedPublicKey
|
||||
profileButton.publicKey = publicKey
|
||||
profileButton.update()
|
||||
profileButton.setOnClickListener { openSettings() }
|
||||
pathStatusViewContainer.setOnClickListener { showPath() }
|
||||
@ -119,7 +119,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
homeAdapter.conversationClickListener = this
|
||||
recyclerView.adapter = homeAdapter
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
ItemTouchHelper(SwipeCallback(this)).attachToRecyclerView(recyclerView)
|
||||
// Set up empty state view
|
||||
createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() }
|
||||
// This is a workaround for the fact that CursorRecyclerViewAdapter doesn't actually auto-update (even though it says it will)
|
||||
@ -157,62 +156,66 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
|
||||
val userDB = DatabaseFactory.getLokiUserDatabase(this)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
val sessionResetImpl = LokiSessionResetImplementation(this)
|
||||
val sessionResetImpl = SessionResetImplementation(this)
|
||||
if (userPublicKey != null) {
|
||||
FriendRequestProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
|
||||
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
application.lokiPublicChatManager.startPollersIfNeeded()
|
||||
application.publicChatManager.startPollersIfNeeded()
|
||||
}
|
||||
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, threadDB, application)
|
||||
MultiDeviceProtocol.configureIfNeeded(apiDB)
|
||||
IP2Country.configureIfNeeded(this)
|
||||
// TODO: Temporary hack to unbork existing clients
|
||||
val allContacts = DatabaseFactory.getRecipientDatabase(this).allAddresses.map {
|
||||
MultiDeviceProtocol.shared.getMasterDevice(it.serialize()) ?: it.serialize()
|
||||
// Preload device links to make message sending quicker
|
||||
val publicKeys = ContactUtilities.getAllContacts(this).filter { contact ->
|
||||
!contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave
|
||||
}.map {
|
||||
it.recipient.address.toPhoneString()
|
||||
}.toSet()
|
||||
val lokiMessageDB = DatabaseFactory.getLokiMessageDatabase(this)
|
||||
for (contact in allContacts) {
|
||||
val slaveDeviceHasPendingFR = MultiDeviceProtocol.shared.getSlaveDevices(contact).any {
|
||||
val slaveDeviceThreadID = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(recipient(this, it))
|
||||
DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(slaveDeviceThreadID) == LokiThreadFriendRequestStatus.REQUEST_RECEIVED
|
||||
}
|
||||
val masterDeviceThreadID = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(recipient(this, contact))
|
||||
val masterDeviceHasNoPendingFR = (DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(masterDeviceThreadID) == LokiThreadFriendRequestStatus.NONE)
|
||||
if (slaveDeviceHasPendingFR && masterDeviceHasNoPendingFR) {
|
||||
val lastMessageID = org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol.getLastMessageID(this, masterDeviceThreadID)
|
||||
if (lastMessageID != null) {
|
||||
val lastMessageFRStatus = lokiMessageDB.getFriendRequestStatus(lastMessageID)
|
||||
if (lastMessageFRStatus != LokiMessageFriendRequestStatus.REQUEST_PENDING) {
|
||||
lokiMessageDB.setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
|
||||
}
|
||||
}
|
||||
FileServerAPI.shared.getDeviceLinks(publicKeys)
|
||||
// Observe blocked contacts changed events
|
||||
val broadcastReceiver = object : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
recyclerView.adapter!!.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
this.broadcastReceiver = broadcastReceiver
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged"))
|
||||
// Clear all data if this is a secondary device
|
||||
if (TextSecurePreferences.getMasterHexEncodedPublicKey(this) != null) {
|
||||
TextSecurePreferences.setWasUnlinked(this, true)
|
||||
ApplicationContext.getInstance(this).clearData()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (TextSecurePreferences.getLocalNumber(this) == null) { return; } // This can be the case after a secondary device is auto-cleared
|
||||
profileButton.update()
|
||||
val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null)
|
||||
val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this)
|
||||
if (hasViewedSeed || !isMasterDevice) {
|
||||
seedReminderView.visibility = View.GONE
|
||||
}
|
||||
if (!TextSecurePreferences.hasSeenPNModeSheet(this)) {
|
||||
val bottomSheet = PNModeBottomSheet()
|
||||
bottomSheet.onConfirmTapped = { isUsingFCM ->
|
||||
TextSecurePreferences.setHasSeenPNModeSheet(this, true)
|
||||
TextSecurePreferences.setIsUsingFCM(this, isUsingFCM)
|
||||
ApplicationContext.getInstance(this).registerForFCMIfNeeded(true)
|
||||
bottomSheet.dismiss()
|
||||
val hasSeenMultiDeviceRemovalSheet = TextSecurePreferences.getHasSeenMultiDeviceRemovalSheet(this)
|
||||
if (!hasSeenMultiDeviceRemovalSheet) {
|
||||
TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey)
|
||||
if (deviceLinks.isNotEmpty()) {
|
||||
val bottomSheet = MultiDeviceRemovalBottomSheet()
|
||||
bottomSheet.onOKTapped = {
|
||||
bottomSheet.dismiss()
|
||||
}
|
||||
bottomSheet.onLinkTapped = {
|
||||
bottomSheet.dismiss()
|
||||
val url = "https://getsession.org/faq"
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
startActivity(intent)
|
||||
}
|
||||
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
||||
}
|
||||
bottomSheet.onSkipTapped = {
|
||||
TextSecurePreferences.setHasSeenPNModeSheet(this, true)
|
||||
bottomSheet.dismiss()
|
||||
}
|
||||
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +225,14 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
createNewPrivateChat()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
val broadcastReceiver = this.broadcastReceiver
|
||||
if (broadcastReceiver != null) {
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver)
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Updating
|
||||
@ -243,7 +254,104 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
}
|
||||
|
||||
override fun onLongConversationClick(view: ConversationView) {
|
||||
// Do nothing
|
||||
val thread = view.thread ?: return
|
||||
val bottomSheet = ConversationOptionsBottomSheet()
|
||||
bottomSheet.recipient = thread.recipient
|
||||
bottomSheet.onBlockOrUnblockTapped = {
|
||||
bottomSheet.dismiss()
|
||||
if (thread.recipient.isBlocked) {
|
||||
unblockConversation(thread)
|
||||
} else {
|
||||
blockConversation(thread)
|
||||
}
|
||||
}
|
||||
bottomSheet.onDeleteTapped = {
|
||||
bottomSheet.dismiss()
|
||||
deleteConversation(thread)
|
||||
}
|
||||
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
||||
}
|
||||
|
||||
private fun blockConversation(thread: ThreadRecord) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question)
|
||||
.setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ ->
|
||||
Thread {
|
||||
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true)
|
||||
ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob())
|
||||
Util.runOnMain {
|
||||
recyclerView.adapter!!.notifyDataSetChanged()
|
||||
dialog.dismiss()
|
||||
}
|
||||
}.start()
|
||||
}.show()
|
||||
}
|
||||
|
||||
private fun unblockConversation(thread: ThreadRecord) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question)
|
||||
.setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ ->
|
||||
Thread {
|
||||
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false)
|
||||
ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob())
|
||||
Util.runOnMain {
|
||||
recyclerView.adapter!!.notifyDataSetChanged()
|
||||
dialog.dismiss()
|
||||
}
|
||||
}.start()
|
||||
}.show()
|
||||
}
|
||||
|
||||
private fun deleteConversation(thread: ThreadRecord) {
|
||||
val threadID = thread.threadId
|
||||
val recipient = thread.recipient
|
||||
val threadDB = DatabaseFactory.getThreadDatabase(this)
|
||||
val deleteThread = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
AsyncTask.execute {
|
||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(this@HomeActivity).getPublicChat(threadID)
|
||||
if (publicChat != null) {
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(this@HomeActivity)
|
||||
apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server)
|
||||
apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
|
||||
ApplicationContext.getInstance(this@HomeActivity).publicChatAPI!!.leave(publicChat.channel, publicChat.server)
|
||||
}
|
||||
threadDB.deleteConversation(threadID)
|
||||
ApplicationContext.getInstance(this@HomeActivity).messageNotifier.updateNotification(this@HomeActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
dialog.setMessage(dialogMessage)
|
||||
dialog.setPositiveButton(R.string.yes) { _, _ ->
|
||||
val isClosedGroup = recipient.address.isClosedGroup
|
||||
// Send a leave group message if this is an active closed group
|
||||
if (isClosedGroup && DatabaseFactory.getGroupDatabase(this).isActive(recipient.address.toGroupString())) {
|
||||
if (!ClosedGroupsProtocol.leaveGroup(this, recipient)) {
|
||||
Toast.makeText(this, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
}
|
||||
// Archive the conversation and then delete it after 10 seconds (the case where the
|
||||
// app was closed before the conversation could be deleted is handled in onCreate)
|
||||
threadDB.archiveConversation(threadID)
|
||||
val delay = if (isClosedGroup) 10000L else 1000L
|
||||
val handler = Handler()
|
||||
handler.postDelayed(deleteThread, delay)
|
||||
// Notify the user
|
||||
val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
|
||||
Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
dialog.setNegativeButton(R.string.no) { _, _ ->
|
||||
// Do nothing
|
||||
}
|
||||
dialog.create().show()
|
||||
}
|
||||
|
||||
private fun openConversation(thread: ThreadRecord) {
|
||||
@ -281,92 +389,5 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
val intent = Intent(this, JoinPublicChatActivity::class.java)
|
||||
show(intent)
|
||||
}
|
||||
|
||||
private class SwipeCallback(val activity: HomeActivity) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
viewHolder as HomeAdapter.ViewHolder
|
||||
val threadID = viewHolder.view.thread!!.threadId
|
||||
val recipient = viewHolder.view.thread!!.recipient
|
||||
val threadDatabase = DatabaseFactory.getThreadDatabase(activity)
|
||||
val deleteThread = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
AsyncTask.execute {
|
||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(activity).getPublicChat(threadID)
|
||||
if (publicChat != null) {
|
||||
val apiDatabase = DatabaseFactory.getLokiAPIDatabase(activity)
|
||||
apiDatabase.removeLastMessageServerID(publicChat.channel, publicChat.server)
|
||||
apiDatabase.removeLastDeletionServerID(publicChat.channel, publicChat.server)
|
||||
ApplicationContext.getInstance(activity).lokiPublicChatAPI!!.leave(publicChat.channel, publicChat.server)
|
||||
}
|
||||
threadDatabase.deleteConversation(threadID)
|
||||
MessageNotifier.updateNotification(activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message
|
||||
val dialog = AlertDialog.Builder(activity)
|
||||
dialog.setMessage(dialogMessage)
|
||||
dialog.setPositiveButton(R.string.yes) { _, _ ->
|
||||
val isClosedGroup = recipient.address.isClosedGroup
|
||||
// Send a leave group message if this is an active closed group
|
||||
if (isClosedGroup && DatabaseFactory.getGroupDatabase(activity).isActive(recipient.address.toGroupString())) {
|
||||
if (!ClosedGroupsProtocol.leaveGroup(activity, recipient)) {
|
||||
Toast.makeText(activity, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show()
|
||||
clearView(activity.recyclerView, viewHolder)
|
||||
return@setPositiveButton
|
||||
}
|
||||
}
|
||||
// Archive the conversation and then delete it after 10 seconds (the case where the
|
||||
// app was closed before the conversation could be deleted is handled in onCreate)
|
||||
threadDatabase.archiveConversation(threadID)
|
||||
val delay = if (isClosedGroup) 10000L else 1000L
|
||||
val handler = Handler()
|
||||
handler.postDelayed(deleteThread, delay)
|
||||
// Notify the user
|
||||
val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
|
||||
Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
dialog.setNegativeButton(R.string.no) { _, _ ->
|
||||
clearView(activity.recyclerView, viewHolder)
|
||||
}
|
||||
dialog.create().show()
|
||||
}
|
||||
|
||||
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dx: Float, dy: Float, actionState: Int, isCurrentlyActive: Boolean) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && dx < 0) {
|
||||
val itemView = viewHolder.itemView
|
||||
animate(viewHolder, dx)
|
||||
val backgroundPaint = Paint()
|
||||
backgroundPaint.color = activity.resources.getColorWithID(R.color.destructive, activity.theme)
|
||||
c.drawRect(itemView.right.toFloat() - abs(dx), itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat(), backgroundPaint)
|
||||
val icon = BitmapFactory.decodeResource(activity.resources, R.drawable.ic_trash_filled_32)
|
||||
val iconPaint = Paint()
|
||||
val left = itemView.right.toFloat() - abs(dx) + activity.resources.getDimension(R.dimen.medium_spacing)
|
||||
val top = itemView.top.toFloat() + (itemView.bottom.toFloat() - itemView.top.toFloat() - icon.height) / 2
|
||||
c.drawBitmap(icon, left, top, iconPaint)
|
||||
} else {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dx, dy, actionState, isCurrentlyActive)
|
||||
}
|
||||
}
|
||||
|
||||
private fun animate(viewHolder: RecyclerView.ViewHolder, dx: Float) {
|
||||
val alpha = 1.0f - abs(dx) / viewHolder.itemView.width.toFloat()
|
||||
viewHolder.itemView.alpha = alpha
|
||||
viewHolder.itemView.translationX = dx
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
viewHolder.itemView.alpha = 1.0f
|
||||
viewHolder.itemView.translationX = 0.0f
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.database.IdentityDatabase
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog
|
||||
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate
|
||||
import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.push
|
||||
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
||||
@ -27,12 +27,11 @@ import org.whispersystems.curve25519.Curve25519
|
||||
import org.whispersystems.libsignal.ecc.Curve
|
||||
import org.whispersystems.libsignal.ecc.ECKeyPair
|
||||
import org.whispersystems.libsignal.util.KeyHelper
|
||||
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
|
||||
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
||||
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
|
||||
|
||||
@ -45,7 +44,7 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
||||
fakeChatView.startAnimating()
|
||||
registerButton.setOnClickListener { register() }
|
||||
restoreButton.setOnClickListener { restore() }
|
||||
linkButton.setOnClickListener { linkDevice() }
|
||||
// linkButton.setOnClickListener { linkDevice() }
|
||||
if (TextSecurePreferences.getWasUnlinked(this)) {
|
||||
Toast.makeText(this, R.string.activity_landing_device_unlinked_dialog_title, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
@ -110,11 +109,10 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
||||
val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
|
||||
val userDB = DatabaseFactory.getLokiUserDatabase(this)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
val sessionResetImpl = LokiSessionResetImplementation(this)
|
||||
FriendRequestProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
val sessionResetImpl = SessionResetImplementation(this)
|
||||
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
|
||||
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB)
|
||||
org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB)
|
||||
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, threadDB, application)
|
||||
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
|
||||
application.setUpP2PAPIIfNeeded()
|
||||
@ -124,13 +122,13 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
||||
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
|
||||
AsyncTask.execute {
|
||||
retryIfNeeded(8) {
|
||||
MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterHexEncodedPublicKey, deviceLink)
|
||||
MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterPublicKey, deviceLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
||||
TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey)
|
||||
TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterPublicKey)
|
||||
val intent = Intent(this, HomeActivity::class.java)
|
||||
show(intent)
|
||||
finish()
|
||||
@ -145,5 +143,7 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
||||
TextSecurePreferences.removeLocalNumber(this)
|
||||
TextSecurePreferences.setHasSeenWelcomeScreen(this, false)
|
||||
TextSecurePreferences.setPromptedPushRegistration(this, false)
|
||||
val application = ApplicationContext.getInstance(this)
|
||||
application.stopPolling()
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ import org.thoughtcrime.securesms.loki.utilities.recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
@ -126,30 +126,30 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(this)
|
||||
val deviceLinks = apiDB.getDeviceLinks(userPublicKey)
|
||||
val deviceLink = deviceLinks.find { it.masterHexEncodedPublicKey == userPublicKey && it.slaveHexEncodedPublicKey == slaveDevicePublicKey }
|
||||
val deviceLink = deviceLinks.find { it.masterPublicKey == userPublicKey && it.slavePublicKey == slaveDevicePublicKey }
|
||||
if (deviceLink == null) {
|
||||
return Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
LokiFileServerAPI.shared.setDeviceLinks(setOf()).successUi {
|
||||
FileServerAPI.shared.setDeviceLinks(setOf()).successUi {
|
||||
DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userPublicKey)
|
||||
deviceLinks.forEach { deviceLink ->
|
||||
// We don't use PushEphemeralMessageJob because want these messages to send before the pre key and
|
||||
// session associated with the slave device have been deleted
|
||||
val unlinkingRequest = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(System.currentTimeMillis())
|
||||
.asUnlinkingRequest(true)
|
||||
.asDeviceUnlinkingRequest(true)
|
||||
val messageSender = ApplicationContext.getInstance(this@LinkedDevicesActivity).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(deviceLink.slaveHexEncodedPublicKey)
|
||||
val address = SignalServiceAddress(deviceLink.slavePublicKey)
|
||||
try {
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slaveHexEncodedPublicKey))
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slavePublicKey))
|
||||
messageSender.sendMessage(0, address, udAccess, unlinkingRequest.build()) // The message ID doesn't matter
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send unlinking request due to error: $e.")
|
||||
throw e
|
||||
}
|
||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slavePublicKey)
|
||||
val sessionStore = TextSecureSessionStore(this@LinkedDevicesActivity)
|
||||
sessionStore.deleteAllSessions(deviceLink.slaveHexEncodedPublicKey)
|
||||
sessionStore.deleteAllSessions(deviceLink.slavePublicKey)
|
||||
}
|
||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
||||
Toast.makeText(this, R.string.activity_linked_devices_unlinking_successful_message, Toast.LENGTH_LONG).show()
|
||||
|
@ -7,7 +7,7 @@ import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
import java.io.File
|
||||
|
||||
class LinkedDevicesLoader(context: Context) : AsyncLoader<List<Device>>(context) {
|
||||
|
@ -3,10 +3,14 @@ package org.thoughtcrime.securesms.loki.activities
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.TransitionDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.annotation.DrawableRes
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.activity_display_name.registerButton
|
||||
import kotlinx.android.synthetic.main.activity_pn_mode.*
|
||||
import network.loki.messenger.R
|
||||
@ -28,6 +32,11 @@ class PNModeActivity : BaseActionBarActivity() {
|
||||
backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() }
|
||||
registerButton.setOnClickListener { register() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_pn_mode, menu)
|
||||
return true
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Animation
|
||||
@ -39,6 +48,25 @@ class PNModeActivity : BaseActionBarActivity() {
|
||||
// endregion
|
||||
|
||||
// region Interaction
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
when(id) {
|
||||
R.id.learnMoreButton -> learnMore()
|
||||
else -> { /* Do nothing */ }
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun learnMore() {
|
||||
try {
|
||||
val url = "https://getsession.org/faq/#privacy"
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFCM() {
|
||||
when (selectedOptionView) {
|
||||
null -> {
|
||||
@ -87,7 +115,7 @@ class PNModeActivity : BaseActionBarActivity() {
|
||||
TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
|
||||
TextSecurePreferences.setPromptedPushRegistration(this, true)
|
||||
TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView))
|
||||
TextSecurePreferences.setHasSeenPNModeSheet(this, true) // Shouldn't be shown to users who've done the new onboarding
|
||||
TextSecurePreferences.setHasSeenMultiDeviceRemovalSheet(this)
|
||||
val application = ApplicationContext.getInstance(this)
|
||||
application.setUpStorageAPIIfNeeded()
|
||||
application.setUpP2PAPIIfNeeded()
|
||||
|
@ -121,6 +121,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
||||
titleTextView.setTextColor(resources.getColorWithID(R.color.text, theme))
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.medium_font_size))
|
||||
titleTextView.text = title
|
||||
titleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
||||
val titleContainer = LinearLayout(this)
|
||||
titleContainer.orientation = LinearLayout.VERTICAL
|
||||
titleContainer.addView(titleTextView)
|
||||
@ -133,6 +134,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
||||
subtitleTextView.setTextColor(resources.getColorWithID(R.color.text, theme))
|
||||
subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.small_font_size))
|
||||
subtitleTextView.text = subtitle
|
||||
subtitleTextView.textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
||||
titleContainer.addView(subtitleTextView)
|
||||
}
|
||||
return mainContainer
|
||||
|
@ -8,9 +8,8 @@ class SelectContactsLoader(context: Context) : AsyncLoader<List<String>>(context
|
||||
|
||||
override fun loadInBackground(): List<String> {
|
||||
val contacts = ContactUtilities.getAllContacts(context)
|
||||
// Only show the master devices of the users we are friends with
|
||||
return contacts.filter { contact ->
|
||||
!contact.recipient.isGroupRecipient && contact.isFriend && !contact.isOurDevice && !contact.isSlave
|
||||
!contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave
|
||||
}.map {
|
||||
it.recipient.address.toPhoneString()
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.security.SecureRandom
|
||||
@ -73,7 +73,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
showQRCodeButton.setOnClickListener { showQRCode() }
|
||||
glide = GlideApp.with(this)
|
||||
profilePictureView.glide = glide
|
||||
profilePictureView.hexEncodedPublicKey = hexEncodedPublicKey
|
||||
profilePictureView.publicKey = hexEncodedPublicKey
|
||||
profilePictureView.isLarge = true
|
||||
profilePictureView.update()
|
||||
profilePictureView.setOnClickListener { showEditProfilePictureUI() }
|
||||
@ -83,16 +83,16 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
copyButton.setOnClickListener { copyPublicKey() }
|
||||
shareButton.setOnClickListener { sharePublicKey() }
|
||||
val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null)
|
||||
linkedDevicesButtonTopSeparator.visibility = View.GONE
|
||||
linkedDevicesButton.visibility = View.GONE
|
||||
if (!isMasterDevice) {
|
||||
linkedDevicesButtonTopSeparator.visibility = View.GONE
|
||||
linkedDevicesButton.visibility = View.GONE
|
||||
seedButtonTopSeparator.visibility = View.GONE
|
||||
seedButton.visibility = View.GONE
|
||||
}
|
||||
privacyButton.setOnClickListener { showPrivacySettings() }
|
||||
notificationsButton.setOnClickListener { showNotificationSettings() }
|
||||
chatsButton.setOnClickListener { showChatSettings() }
|
||||
linkedDevicesButton.setOnClickListener { showLinkedDevices() }
|
||||
// linkedDevicesButton.setOnClickListener { showLinkedDevices() }
|
||||
seedButton.setOnClickListener { showSeed() }
|
||||
clearAllDataButton.setOnClickListener { clearAllData() }
|
||||
versionTextView.text = String.format(getString(R.string.version_s), BuildConfig.VERSION_NAME)
|
||||
@ -151,7 +151,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
val promises = mutableListOf<Promise<*, Exception>>()
|
||||
val displayName = displayNameToBeUploaded
|
||||
if (displayName != null) {
|
||||
val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI
|
||||
val publicChatAPI = ApplicationContext.getInstance(this).publicChatAPI
|
||||
if (publicChatAPI != null) {
|
||||
val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers()
|
||||
promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) })
|
||||
@ -162,7 +162,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
|
||||
val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey)
|
||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||
val storageAPI = LokiFileServerAPI.shared
|
||||
val storageAPI = FileServerAPI.shared
|
||||
val deferred = deferred<Unit, Exception>()
|
||||
AsyncTask.execute {
|
||||
val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong())
|
||||
@ -202,9 +202,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
if (displayName.isEmpty()) {
|
||||
return Toast.makeText(this, R.string.activity_settings_display_name_missing_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
if (!displayName.matches(Regex("[a-zA-Z0-9_]+"))) {
|
||||
return Toast.makeText(this, R.string.activity_settings_invalid_display_name_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) {
|
||||
return Toast.makeText(this, R.string.activity_settings_display_name_too_long_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ import org.thoughtcrime.securesms.jobs.PushContentReceiveJob
|
||||
import org.thoughtcrime.securesms.service.PersistentAlarmManagerListener
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope
|
||||
import org.whispersystems.signalservice.loki.api.LokiAPI
|
||||
import org.whispersystems.signalservice.loki.api.SnodeAPI
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class BackgroundPollWorker : PersistentAlarmManagerListener() {
|
||||
|
||||
companion object {
|
||||
private val pollInterval = TimeUnit.MINUTES.toMillis(15)
|
||||
private val pollInterval = TimeUnit.MINUTES.toMillis(20)
|
||||
|
||||
@JvmStatic
|
||||
fun schedule(context: Context) {
|
||||
@ -29,14 +29,14 @@ class BackgroundPollWorker : PersistentAlarmManagerListener() {
|
||||
|
||||
override fun onAlarm(context: Context, scheduledTime: Long): Long {
|
||||
if (scheduledTime != 0L) {
|
||||
if (TextSecurePreferences.isUsingFCM(context)) {
|
||||
if (!TextSecurePreferences.isUsingFCM(context)) {
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
try {
|
||||
val applicationContext = context.applicationContext as ApplicationContext
|
||||
val broadcaster = applicationContext.broadcaster
|
||||
LokiAPI.configureIfNeeded(userPublicKey, lokiAPIDatabase, broadcaster)
|
||||
LokiAPI.shared.getMessages().map { messages ->
|
||||
SnodeAPI.configureIfNeeded(userPublicKey, lokiAPIDatabase, broadcaster)
|
||||
SnodeAPI.shared.getMessages().map { messages ->
|
||||
messages.forEach {
|
||||
PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false)
|
||||
}
|
||||
@ -47,7 +47,7 @@ class BackgroundPollWorker : PersistentAlarmManagerListener() {
|
||||
}
|
||||
val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { it.value }
|
||||
for (openGroup in openGroups) {
|
||||
val poller = LokiPublicChatPoller(context, openGroup)
|
||||
val poller = PublicChatPoller(context, openGroup)
|
||||
poller.stop()
|
||||
poller.pollForNewMessages()
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import okhttp3.*
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.libsignal.logging.Log
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement
|
||||
import org.whispersystems.signalservice.loki.api.PushNotificationAcknowledgement
|
||||
import java.io.IOException
|
||||
|
||||
object LokiPushNotificationManager {
|
||||
private val connection = OkHttpClient()
|
||||
|
||||
private val server by lazy {
|
||||
LokiPushNotificationAcknowledgement.shared.server
|
||||
PushNotificationAcknowledgement.shared.server
|
||||
}
|
||||
|
||||
private const val tokenExpirationInterval = 12 * 60 * 60 * 1000
|
||||
@ -47,11 +47,11 @@ object LokiPushNotificationManager {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun register(token: String, hexEncodedPublicKey: String, context: Context?, force: Boolean) {
|
||||
fun register(token: String, publicKey: String, context: Context?, force: Boolean) {
|
||||
val oldToken = TextSecurePreferences.getFCMToken(context)
|
||||
val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context)
|
||||
if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
|
||||
val parameters = mapOf( "token" to token, "pubKey" to hexEncodedPublicKey )
|
||||
val parameters = mapOf( "token" to token, "pubKey" to publicKey )
|
||||
val url = "$server/register"
|
||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||
val request = Request.Builder().url(url).post(body).build()
|
||||
|
@ -12,19 +12,37 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
|
||||
|
||||
class LokiPublicChatManager(private val context: Context) {
|
||||
private var chats = mutableMapOf<Long, LokiPublicChat>()
|
||||
private val pollers = mutableMapOf<Long, LokiPublicChatPoller>()
|
||||
class PublicChatManager(private val context: Context) {
|
||||
private var chats = mutableMapOf<Long, PublicChat>()
|
||||
private val pollers = mutableMapOf<Long, PublicChatPoller>()
|
||||
private val observers = mutableMapOf<Long, ContentObserver>()
|
||||
private var isPolling = false
|
||||
|
||||
public fun areAllCaughtUp():Boolean {
|
||||
var areAllCaughtUp = true
|
||||
refreshChatsAndPollers()
|
||||
for ((threadID, chat) in chats) {
|
||||
val poller = pollers[threadID] ?: PublicChatPoller(context, chat)
|
||||
areAllCaughtUp = areAllCaughtUp && poller.isCaughtUp
|
||||
}
|
||||
return areAllCaughtUp
|
||||
}
|
||||
|
||||
public fun markAllAsNotCaughtUp() {
|
||||
refreshChatsAndPollers()
|
||||
for ((threadID, chat) in chats) {
|
||||
val poller = pollers[threadID] ?: PublicChatPoller(context, chat)
|
||||
poller.isCaughtUp = false
|
||||
}
|
||||
}
|
||||
|
||||
public fun startPollersIfNeeded() {
|
||||
refreshChatsAndPollers()
|
||||
|
||||
for ((threadId, chat) in chats) {
|
||||
val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat)
|
||||
val poller = pollers[threadId] ?: PublicChatPoller(context, chat)
|
||||
poller.startIfNeeded()
|
||||
listenToThreadDeletion(threadId)
|
||||
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }
|
||||
@ -37,8 +55,8 @@ class LokiPublicChatManager(private val context: Context) {
|
||||
isPolling = false
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long): Promise<LokiPublicChat, Exception> {
|
||||
val groupChatAPI = ApplicationContext.getInstance(context).lokiPublicChatAPI ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
|
||||
public fun addChat(server: String, channel: Long): Promise<PublicChat, Exception> {
|
||||
val groupChatAPI = ApplicationContext.getInstance(context).publicChatAPI ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
|
||||
return groupChatAPI.getAuthToken(server).bind {
|
||||
groupChatAPI.getChannelInfo(channel, server)
|
||||
}.map {
|
||||
@ -46,8 +64,8 @@ class LokiPublicChatManager(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long, name: String): LokiPublicChat {
|
||||
val chat = LokiPublicChat(channel, server, name, true)
|
||||
public fun addChat(server: String, channel: Long, name: String): PublicChat {
|
||||
val chat = PublicChat(channel, server, name, true)
|
||||
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
|
||||
// Create the group if we don't have one
|
||||
if (threadID < 0) {
|
||||
@ -58,7 +76,7 @@ class LokiPublicChatManager(private val context: Context) {
|
||||
// Set our name on the server
|
||||
val displayName = TextSecurePreferences.getProfileName(context)
|
||||
if (!TextUtils.isEmpty(displayName)) {
|
||||
ApplicationContext.getInstance(context).lokiPublicChatAPI?.setDisplayName(displayName, server)
|
||||
ApplicationContext.getInstance(context).publicChatAPI?.setDisplayName(displayName, server)
|
||||
}
|
||||
// Start polling
|
||||
Util.runOnMain{ startPollersIfNeeded() }
|
@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol
|
||||
import org.thoughtcrime.securesms.loki.utilities.successBackground
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
@ -22,29 +23,29 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatMessage
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
|
||||
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatAPI
|
||||
import org.whispersystems.signalservice.loki.api.opengroups.PublicChatMessage
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
class LokiPublicChatPoller(private val context: Context, private val group: LokiPublicChat) {
|
||||
class PublicChatPoller(private val context: Context, private val group: PublicChat) {
|
||||
private val handler = Handler()
|
||||
private var hasStarted = false
|
||||
public var isCaughtUp = false
|
||||
|
||||
// region Convenience
|
||||
private val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
private var displayNameUpdatees = setOf<String>()
|
||||
|
||||
private val api: LokiPublicChatAPI
|
||||
private val api: PublicChatAPI
|
||||
get() = {
|
||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context)
|
||||
LokiPublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase)
|
||||
PublicChatAPI(userHexEncodedPublicKey, userPrivateKey, lokiAPIDatabase, lokiUserDatabase)
|
||||
}()
|
||||
// endregion
|
||||
|
||||
@ -111,16 +112,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
// endregion
|
||||
|
||||
// region Polling
|
||||
private fun getDataMessage(message: LokiPublicChatMessage): SignalServiceDataMessage {
|
||||
private fun getDataMessage(message: PublicChatMessage): SignalServiceDataMessage {
|
||||
val id = group.id.toByteArray()
|
||||
val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.PUBLIC_CHAT, null, null, null, null)
|
||||
val quote = if (message.quote != null) {
|
||||
SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf())
|
||||
SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteePublicKey), message.quote!!.quotedMessageBody, listOf())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val attachments = message.attachments.mapNotNull { attachment ->
|
||||
if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null }
|
||||
if (attachment.kind != PublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null }
|
||||
SignalServiceAttachmentPointer(
|
||||
attachment.serverID,
|
||||
attachment.contentType,
|
||||
@ -134,7 +135,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
Optional.fromNullable(attachment.caption),
|
||||
attachment.url)
|
||||
}
|
||||
val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview }
|
||||
val linkPreview = message.attachments.firstOrNull { it.kind == PublicChatMessage.Attachment.Kind.LinkPreview }
|
||||
val signalLinkPreviews = mutableListOf<SignalServiceDataMessage.Preview>()
|
||||
if (linkPreview != null) {
|
||||
val attachment = SignalServiceAttachmentPointer(
|
||||
@ -156,16 +157,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
}
|
||||
|
||||
fun pollForNewMessages() {
|
||||
fun processIncomingMessage(message: LokiPublicChatMessage) {
|
||||
fun processIncomingMessage(message: PublicChatMessage) {
|
||||
// If the sender of the current message is not a slave device, set the display name in the database
|
||||
val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.hexEncodedPublicKey)
|
||||
val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.senderPublicKey)
|
||||
if (masterHexEncodedPublicKey == null) {
|
||||
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
|
||||
val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})"
|
||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName)
|
||||
}
|
||||
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.hexEncodedPublicKey
|
||||
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.senderPublicKey
|
||||
val serviceDataMessage = getDataMessage(message)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false, false, false, false)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false)
|
||||
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
||||
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||
} else {
|
||||
@ -181,22 +182,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
database.setProfileKey(senderAsRecipient, profileKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url))
|
||||
}
|
||||
} else if (senderAsRecipient.profileAvatar.orEmpty().isNotEmpty()) {
|
||||
// Clear the profile picture if we had a profile picture before and we're not friends with the person
|
||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(senderAsRecipient)
|
||||
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID)
|
||||
if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
|
||||
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
fun processOutgoingMessage(message: LokiPublicChatMessage) {
|
||||
fun processOutgoingMessage(message: PublicChatMessage) {
|
||||
val messageServerID = message.serverID ?: return
|
||||
val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
|
||||
if (isDuplicate) { return }
|
||||
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
|
||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
val dataMessage = getDataMessage(message)
|
||||
SessionMetaProtocol.dropFromTimestampCacheIfNeeded(dataMessage.timestamp)
|
||||
val transcript = SentTranscriptMessage(userHexEncodedPublicKey, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false))
|
||||
transcript.messageServerID = messageServerID
|
||||
if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
|
||||
@ -205,7 +200,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript)
|
||||
}
|
||||
// If we got a message from our master device then make sure our mapping stays in sync
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(message.hexEncodedPublicKey), false)
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(message.senderPublicKey), false)
|
||||
if (recipient.isUserMasterDevice && message.profilePicture != null) {
|
||||
val profileKey = message.profilePicture!!.profileKey
|
||||
val url = message.profilePicture!!.url
|
||||
@ -221,34 +216,39 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
var uniqueDevices = setOf<String>()
|
||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
LokiFileServerAPI.configure(false, userHexEncodedPublicKey, userPrivateKey, apiDB)
|
||||
FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
|
||||
// Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below
|
||||
api.getMessages(group.channel, group.server).bind(LokiPublicChatAPI.sharedContext) { messages ->
|
||||
api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages ->
|
||||
/*
|
||||
if (messages.isNotEmpty()) {
|
||||
// We need to fetch the device mapping for any devices we don't have
|
||||
uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet()
|
||||
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) }
|
||||
uniqueDevices = messages.map { it.senderPublicKey }.toSet()
|
||||
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && FileServerAPI.shared.hasDeviceLinkCacheExpired(publicKey = it) }
|
||||
if (devicesToUpdate.isNotEmpty()) {
|
||||
return@bind LokiFileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages }
|
||||
return@bind FileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages }
|
||||
}
|
||||
}
|
||||
*/
|
||||
Promise.of(messages)
|
||||
}.successBackground {
|
||||
/*
|
||||
val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
|
||||
// This will return null if the current device is a master device
|
||||
MultiDeviceProtocol.shared.getMasterDevice(it)
|
||||
}.toSet()
|
||||
// Fetch the display names of the master devices
|
||||
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
|
||||
*/
|
||||
}.successBackground { messages ->
|
||||
// Process messages in the background
|
||||
messages.forEach { message ->
|
||||
if (userDevices.contains(message.hexEncodedPublicKey)) {
|
||||
if (userDevices.contains(message.senderPublicKey)) {
|
||||
processOutgoingMessage(message)
|
||||
} else {
|
||||
processIncomingMessage(message)
|
||||
}
|
||||
}
|
||||
isCaughtUp = true
|
||||
}.fail {
|
||||
Log.d("Loki", "Failed to get messages for group chat with ID: ${group.channel} on server: ${group.server}.")
|
||||
}
|
@ -7,15 +7,15 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.libsignal.logging.Log
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope
|
||||
import org.whispersystems.signalservice.internal.util.Base64
|
||||
import org.whispersystems.signalservice.loki.api.LokiMessageWrapper
|
||||
import org.whispersystems.signalservice.loki.api.MessageWrapper
|
||||
|
||||
class PushNotificationService : FirebaseMessagingService() {
|
||||
|
||||
override fun onNewToken(token: String) {
|
||||
super.onNewToken(token)
|
||||
Log.d("Loki", "New FCM token: $token.")
|
||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return
|
||||
LokiPushNotificationManager.register(token, userHexEncodedPublicKey, this, false)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return
|
||||
LokiPushNotificationManager.register(token, userPublicKey, this, false)
|
||||
}
|
||||
|
||||
override fun onMessageReceived(message: RemoteMessage) {
|
||||
@ -23,7 +23,7 @@ class PushNotificationService : FirebaseMessagingService() {
|
||||
val data = base64EncodedData?.let { Base64.decode(it) }
|
||||
if (data != null) {
|
||||
try {
|
||||
val envelope = LokiMessageWrapper.unwrap(data)
|
||||
val envelope = MessageWrapper.unwrap(data)
|
||||
PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope), true)
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to unwrap data for message.")
|
||||
|
@ -8,15 +8,18 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.api.LokiAPITarget
|
||||
import org.whispersystems.signalservice.loki.api.Snode
|
||||
import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink
|
||||
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLink
|
||||
|
||||
class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol {
|
||||
|
||||
private val userPublicKey get() = TextSecurePreferences.getLocalNumber(context)
|
||||
|
||||
companion object {
|
||||
// Shared
|
||||
private val publicKey = "public_key"
|
||||
private val timestamp = "timestamp"
|
||||
// Snode pool cache
|
||||
private val snodePoolCache = "loki_snode_pool_cache"
|
||||
private val dummyKey = "dummy_key"
|
||||
@ -29,9 +32,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
@JvmStatic val createOnionRequestPathCacheCommand = "CREATE TABLE $onionRequestPathCache ($indexPath TEXT PRIMARY KEY, $snode TEXT);"
|
||||
// Swarm cache
|
||||
private val swarmCache = "loki_api_swarm_cache"
|
||||
private val hexEncodedPublicKey = "hex_encoded_public_key"
|
||||
private val swarmPublicKey = "hex_encoded_public_key"
|
||||
private val swarm = "swarm"
|
||||
@JvmStatic val createSwarmCacheCommand = "CREATE TABLE $swarmCache ($hexEncodedPublicKey TEXT PRIMARY KEY, $swarm TEXT);"
|
||||
@JvmStatic val createSwarmCacheCommand = "CREATE TABLE $swarmCache ($swarmPublicKey TEXT PRIMARY KEY, $swarm TEXT);"
|
||||
// Last message hash value cache
|
||||
private val lastMessageHashValueCache = "loki_api_last_message_hash_value_cache"
|
||||
private val target = "target"
|
||||
@ -59,25 +62,36 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
@JvmStatic val createLastDeletionServerIDCacheCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
|
||||
// Device link cache
|
||||
private val deviceLinkCache = "loki_pairing_authorisation_cache"
|
||||
private val masterHexEncodedPublicKey = "primary_device"
|
||||
private val slaveHexEncodedPublicKey = "secondary_device"
|
||||
private val masterPublicKey = "primary_device"
|
||||
private val slavePublicKey = "secondary_device"
|
||||
private val requestSignature = "request_signature"
|
||||
private val authorizationSignature = "grant_signature"
|
||||
@JvmStatic val createDeviceLinkCacheCommand = "CREATE TABLE $deviceLinkCache ($masterHexEncodedPublicKey TEXT, $slaveHexEncodedPublicKey TEXT, " +
|
||||
"$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterHexEncodedPublicKey, $slaveHexEncodedPublicKey));"
|
||||
@JvmStatic val createDeviceLinkCacheCommand = "CREATE TABLE $deviceLinkCache ($masterPublicKey TEXT, $slavePublicKey TEXT, " +
|
||||
"$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterPublicKey, $slavePublicKey));"
|
||||
// User count cache
|
||||
private val userCountCache = "loki_user_count_cache"
|
||||
private val publicChatID = "public_chat_id"
|
||||
private val userCount = "user_count"
|
||||
@JvmStatic val createUserCountCacheCommand = "CREATE TABLE $userCountCache ($publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0);"
|
||||
// Session request timestamp cache
|
||||
// Session request sent timestamp cache
|
||||
private val sessionRequestSentTimestampCache = "session_request_sent_timestamp_cache"
|
||||
@JvmStatic val createSessionRequestSentTimestampCacheCommand = "CREATE TABLE $sessionRequestSentTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);"
|
||||
// Session request processed timestamp cache
|
||||
private val sessionRequestProcessedTimestampCache = "session_request_processed_timestamp_cache"
|
||||
@JvmStatic val createSessionRequestProcessedTimestampCacheCommand = "CREATE TABLE $sessionRequestProcessedTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);"
|
||||
// Open group public keys
|
||||
private val openGroupPublicKeyDB = "open_group_public_keys"
|
||||
@JvmStatic val createOpenGroupPublicKeyDBCommand = "CREATE TABLE $openGroupPublicKeyDB ($server STRING PRIMARY KEY, $publicKey INTEGER DEFAULT 0);"
|
||||
|
||||
|
||||
|
||||
// region Deprecated
|
||||
private val sessionRequestTimestampCache = "session_request_timestamp_cache"
|
||||
private val publicKey = "public_key"
|
||||
private val timestamp = "timestamp"
|
||||
@JvmStatic val createSessionRequestTimestampCacheCommand = "CREATE TABLE $sessionRequestTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createSessionRequestTimestampCacheCommand = "CREATE TABLE $sessionRequestTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp STRING);"
|
||||
// endregion
|
||||
}
|
||||
|
||||
override fun getSnodePool(): Set<LokiAPITarget> {
|
||||
override fun getSnodePool(): Set<Snode> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(snodePoolCache, "${Companion.dummyKey} = ?", wrap("dummy_key")) { cursor ->
|
||||
val snodePoolAsString = cursor.getString(cursor.getColumnIndexOrThrow(snodePool))
|
||||
@ -87,12 +101,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
||||
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
||||
val x25519Key = components.getOrNull(3) ?: return@mapNotNull null
|
||||
LokiAPITarget(address, port, LokiAPITarget.KeySet(ed25519Key, x25519Key))
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
||||
}
|
||||
}?.toSet() ?: setOf()
|
||||
}
|
||||
|
||||
override fun setSnodePool(newValue: Set<LokiAPITarget>) {
|
||||
override fun setSnodePool(newValue: Set<Snode>) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val snodePoolAsString = newValue.joinToString(", ") { snode ->
|
||||
var string = "${snode.address}-${snode.port}"
|
||||
@ -106,9 +120,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(snodePoolCache, row, "${Companion.dummyKey} = ?", wrap("dummy_key"))
|
||||
}
|
||||
|
||||
override fun getOnionRequestPaths(): List<List<LokiAPITarget>> {
|
||||
override fun getOnionRequestPaths(): List<List<Snode>> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
fun get(indexPath: String): LokiAPITarget? {
|
||||
fun get(indexPath: String): Snode? {
|
||||
return database.get(onionRequestPathCache, "${Companion.indexPath} = ?", wrap(indexPath)) { cursor ->
|
||||
val snodeAsString = cursor.getString(cursor.getColumnIndexOrThrow(snode))
|
||||
val components = snodeAsString.split("-")
|
||||
@ -117,7 +131,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val ed25519Key = components.getOrNull(2)
|
||||
val x25519Key = components.getOrNull(3)
|
||||
if (port != null && ed25519Key != null && x25519Key != null) {
|
||||
LokiAPITarget(address, port, LokiAPITarget.KeySet(ed25519Key, x25519Key))
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@ -139,7 +153,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
delete("1-1"); delete("1-2")
|
||||
}
|
||||
|
||||
override fun setOnionRequestPaths(newValue: List<List<LokiAPITarget>>) {
|
||||
override fun setOnionRequestPaths(newValue: List<List<Snode>>) {
|
||||
// FIXME: This is a bit of a dirty approach that assumes 2 paths of length 3 each. We should do better than this.
|
||||
if (newValue.count() != 2) { return }
|
||||
val path0 = newValue[0]
|
||||
@ -147,7 +161,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
if (path0.count() != 3 || path1.count() != 3) { return }
|
||||
Log.d("Loki", "Persisting onion request paths to database.")
|
||||
val database = databaseHelper.writableDatabase
|
||||
fun set(indexPath: String ,snode: LokiAPITarget) {
|
||||
fun set(indexPath: String ,snode: Snode) {
|
||||
var snodeAsString = "${snode.address}-${snode.port}"
|
||||
val keySet = snode.publicKeySet
|
||||
if (keySet != null) {
|
||||
@ -161,9 +175,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
set("1-1", path1[1]); set("1-2", path1[2])
|
||||
}
|
||||
|
||||
override fun getSwarm(hexEncodedPublicKey: String): Set<LokiAPITarget>? {
|
||||
override fun getSwarm(publicKey: String): Set<Snode>? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(swarmCache, "${Companion.hexEncodedPublicKey} = ?", wrap(hexEncodedPublicKey)) { cursor ->
|
||||
return database.get(swarmCache, "${Companion.swarmPublicKey} = ?", wrap(publicKey)) { cursor ->
|
||||
val swarmAsString = cursor.getString(cursor.getColumnIndexOrThrow(swarm))
|
||||
swarmAsString.split(", ").mapNotNull { targetAsString ->
|
||||
val components = targetAsString.split("-")
|
||||
@ -171,12 +185,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
||||
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
||||
val x25519Key = components.getOrNull(3) ?: return@mapNotNull null
|
||||
LokiAPITarget(address, port, LokiAPITarget.KeySet(ed25519Key, x25519Key))
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
||||
}
|
||||
}?.toSet()
|
||||
}
|
||||
|
||||
override fun setSwarm(hexEncodedPublicKey: String, newValue: Set<LokiAPITarget>) {
|
||||
override fun setSwarm(publicKey: String, newValue: Set<Snode>) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val swarmAsString = newValue.joinToString(", ") { target ->
|
||||
var string = "${target.address}-${target.port}"
|
||||
@ -186,21 +200,21 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}
|
||||
string
|
||||
}
|
||||
val row = wrap(mapOf(Companion.hexEncodedPublicKey to hexEncodedPublicKey, swarm to swarmAsString))
|
||||
database.insertOrUpdate(swarmCache, row, "${Companion.hexEncodedPublicKey} = ?", wrap(hexEncodedPublicKey))
|
||||
val row = wrap(mapOf(Companion.swarmPublicKey to publicKey, swarm to swarmAsString))
|
||||
database.insertOrUpdate(swarmCache, row, "${Companion.swarmPublicKey} = ?", wrap(publicKey))
|
||||
}
|
||||
|
||||
override fun getLastMessageHashValue(target: LokiAPITarget): String? {
|
||||
override fun getLastMessageHashValue(snode: Snode): String? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(lastMessageHashValueCache, "${Companion.target} = ?", wrap(target.address)) { cursor ->
|
||||
return database.get(lastMessageHashValueCache, "${Companion.target} = ?", wrap(snode.address)) { cursor ->
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(lastMessageHashValue))
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLastMessageHashValue(target: LokiAPITarget, newValue: String) {
|
||||
override fun setLastMessageHashValue(snode: Snode, newValue: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val row = wrap(mapOf(Companion.target to target.address, lastMessageHashValue to newValue))
|
||||
database.insertOrUpdate(lastMessageHashValueCache, row, "${Companion.target} = ?", wrap(target.address))
|
||||
val row = wrap(mapOf(Companion.target to snode.address, lastMessageHashValue to newValue))
|
||||
database.insertOrUpdate(lastMessageHashValueCache, row, "${Companion.target} = ?", wrap(snode.address))
|
||||
}
|
||||
|
||||
override fun getReceivedMessageHashValues(): Set<String>? {
|
||||
@ -277,35 +291,44 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> {
|
||||
override fun getDeviceLinks(publicKey: String): Set<DeviceLink> {
|
||||
return setOf()
|
||||
/*
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.getAll(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
|
||||
val masterHexEncodedPublicKey = cursor.getString(masterHexEncodedPublicKey)
|
||||
val slaveHexEncodedPublicKey = cursor.getString(slaveHexEncodedPublicKey)
|
||||
return database.getAll(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) { cursor ->
|
||||
val masterHexEncodedPublicKey = cursor.getString(masterPublicKey)
|
||||
val slaveHexEncodedPublicKey = cursor.getString(slavePublicKey)
|
||||
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
|
||||
val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
|
||||
DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
|
||||
}.toSet()
|
||||
*/
|
||||
}
|
||||
|
||||
override fun clearDeviceLinks(hexEncodedPublicKey: String) {
|
||||
override fun clearDeviceLinks(publicKey: String) {
|
||||
/*
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
|
||||
database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey ))
|
||||
*/
|
||||
}
|
||||
|
||||
override fun addDeviceLink(deviceLink: DeviceLink) {
|
||||
/*
|
||||
val database = databaseHelper.writableDatabase
|
||||
val values = ContentValues()
|
||||
values.put(masterHexEncodedPublicKey, deviceLink.masterHexEncodedPublicKey)
|
||||
values.put(slaveHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey)
|
||||
values.put(masterPublicKey, deviceLink.masterPublicKey)
|
||||
values.put(slavePublicKey, deviceLink.slavePublicKey)
|
||||
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
|
||||
if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
|
||||
database.insertOrUpdate(deviceLinkCache, values, "$masterHexEncodedPublicKey = ? AND $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
|
||||
database.insertOrUpdate(deviceLinkCache, values, "$masterPublicKey = ? AND $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
|
||||
*/
|
||||
}
|
||||
|
||||
override fun removeDeviceLink(deviceLink: DeviceLink) {
|
||||
/*
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
|
||||
database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
|
||||
*/
|
||||
}
|
||||
|
||||
fun getUserCount(group: Long, server: String): Int? {
|
||||
@ -316,24 +339,50 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}?.toInt()
|
||||
}
|
||||
|
||||
override fun setUserCount(userCount: Int, group: Long, server: String) {
|
||||
override fun setUserCount(group: Long, server: String, newValue: Int) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val index = "$server.$group"
|
||||
val row = wrap(mapOf(publicChatID to index, Companion.userCount to userCount.toString()))
|
||||
val row = wrap(mapOf(publicChatID to index, Companion.userCount to newValue.toString()))
|
||||
database.insertOrUpdate(userCountCache, row, "$publicChatID = ?", wrap(index))
|
||||
}
|
||||
|
||||
override fun getSessionRequestTimestamp(publicKey: String): Long? {
|
||||
override fun getSessionRequestSentTimestamp(publicKey: String): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(sessionRequestTimestampCache, "$LokiAPIDatabase.publicKey = ?", wrap(publicKey)) { cursor ->
|
||||
return database.get(sessionRequestSentTimestampCache, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->
|
||||
cursor.getInt(LokiAPIDatabase.timestamp)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun setSessionRequestTimestamp(publicKey: String, timestamp: Long) {
|
||||
override fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to timestamp.toString()))
|
||||
database.insertOrUpdate(sessionRequestTimestampCache, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey))
|
||||
val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString()))
|
||||
database.insertOrUpdate(sessionRequestSentTimestampCache, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey))
|
||||
}
|
||||
|
||||
override fun getSessionRequestProcessedTimestamp(publicKey: String): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(sessionRequestProcessedTimestampCache, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->
|
||||
cursor.getInt(LokiAPIDatabase.timestamp)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
override fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString()))
|
||||
database.insertOrUpdate(sessionRequestProcessedTimestampCache, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey))
|
||||
}
|
||||
|
||||
override fun getOpenGroupPublicKey(server: String): String? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(openGroupPublicKeyDB, "${LokiAPIDatabase.server} = ?", wrap(server)) { cursor ->
|
||||
cursor.getString(LokiAPIDatabase.publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setOpenGroupPublicKey(server: String, newValue: String) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val row = wrap(mapOf(LokiAPIDatabase.server to server, LokiAPIDatabase.publicKey to newValue))
|
||||
database.insertOrUpdate(openGroupPublicKeyDB, row, "${LokiAPIDatabase.server} = ?", wrap(server))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,39 +11,38 @@ import org.thoughtcrime.securesms.loki.utilities.getInt
|
||||
import org.thoughtcrime.securesms.loki.utilities.getString
|
||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
||||
import org.whispersystems.signalservice.loki.database.LokiMessageDatabaseProtocol
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
|
||||
|
||||
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
||||
|
||||
companion object {
|
||||
private val messageFriendRequestTableName = "loki_message_friend_request_database"
|
||||
private val messageThreadMappingTableName = "loki_message_thread_mapping_database"
|
||||
private val errorMessageTableName = "loki_error_message_database"
|
||||
private val messageIDTable = "loki_message_friend_request_database"
|
||||
private val messageThreadMappingTable = "loki_message_thread_mapping_database"
|
||||
private val errorMessageTable = "loki_error_message_database"
|
||||
private val messageID = "message_id"
|
||||
private val serverID = "server_id"
|
||||
private val friendRequestStatus = "friend_request_status"
|
||||
private val threadID = "thread_id"
|
||||
private val errorMessage = "error_message"
|
||||
@JvmStatic val createMessageFriendRequestTableCommand = "CREATE TABLE $messageFriendRequestTableName ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTableName ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
|
||||
@JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTableName ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
|
||||
@JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
|
||||
@JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
|
||||
@JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
|
||||
}
|
||||
|
||||
override fun getQuoteServerID(quoteID: Long, quoteeHexEncodedPublicKey: String): Long? {
|
||||
val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, Address.fromSerialized(quoteeHexEncodedPublicKey))
|
||||
override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? {
|
||||
val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, Address.fromSerialized(quoteePublicKey))
|
||||
return if (message != null) getServerID(message.getId()) else null
|
||||
}
|
||||
|
||||
fun getServerID(messageID: Long): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageFriendRequestTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
cursor.getInt(serverID)
|
||||
}?.toLong()
|
||||
}
|
||||
|
||||
fun getMessageID(serverID: Long): Long? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageFriendRequestTableName, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
|
||||
return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
|
||||
cursor.getInt(messageID)
|
||||
}?.toLong()
|
||||
}
|
||||
@ -53,12 +52,12 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.serverID, serverID)
|
||||
database.insertOrUpdate(messageFriendRequestTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
}
|
||||
|
||||
fun getOriginalThreadID(messageID: Long): Long {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(messageThreadMappingTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
return database.get(messageThreadMappingTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
cursor.getInt(threadID)
|
||||
}?.toLong() ?: -1L
|
||||
}
|
||||
@ -68,38 +67,12 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.threadID, threadID)
|
||||
database.insertOrUpdate(messageThreadMappingTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
}
|
||||
|
||||
fun getFriendRequestStatus(messageID: Long): LokiMessageFriendRequestStatus {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val result = database.get(messageFriendRequestTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
cursor.getInt(friendRequestStatus)
|
||||
}
|
||||
return if (result != null) {
|
||||
LokiMessageFriendRequestStatus.values().first { it.rawValue == result }
|
||||
} else {
|
||||
LokiMessageFriendRequestStatus.NONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun setFriendRequestStatus(messageID: Long, friendRequestStatus: LokiMessageFriendRequestStatus) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue)
|
||||
database.insertOrUpdate(messageFriendRequestTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
val threadID = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID)
|
||||
notifyConversationListeners(threadID)
|
||||
}
|
||||
|
||||
fun isFriendRequest(messageID: Long): Boolean {
|
||||
return getFriendRequestStatus(messageID) != LokiMessageFriendRequestStatus.NONE
|
||||
database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
}
|
||||
|
||||
fun getErrorMessage(messageID: Long): String? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(errorMessageTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
return database.get(errorMessageTable, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
|
||||
cursor.getString(errorMessage)
|
||||
}
|
||||
}
|
||||
@ -109,6 +82,6 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
val contentValues = ContentValues(2)
|
||||
contentValues.put(Companion.messageID, messageID)
|
||||
contentValues.put(Companion.errorMessage, errorMessage)
|
||||
database.insertOrUpdate(errorMessageTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user