mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Merge branch 'dev'
This commit is contained in:
commit
68785fe44f
@ -199,8 +199,8 @@ dependencies {
|
|||||||
implementation "com.github.ybq:Android-SpinKit:1.4.0"
|
implementation "com.github.ybq:Android-SpinKit:1.4.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 35
|
def canonicalVersionCode = 36
|
||||||
def canonicalVersionName = "1.0.1"
|
def canonicalVersionName = "1.0.2"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
def abiPostFix = ['armeabi-v7a' : 1,
|
def abiPostFix = ['armeabi-v7a' : 1,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="oval">
|
android:shape="oval">
|
||||||
|
|
||||||
<solid android:color="@color/compose_view_background" />
|
<solid android:color="@color/compose_view_background" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@ -30,7 +30,7 @@
|
|||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." />
|
android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.loki.redesign.views.SeparatorView
|
<org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView
|
||||||
android:id="@+id/separatorView"
|
android:id="@+id/separatorView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
|
@ -1,58 +1,59 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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_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_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="32dp"
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
android:layout_marginEnd="32dp"
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
android:orientation="vertical">
|
android:textSize="@dimen/large_font_size"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="Pick your display name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/titleTextView"
|
android:layout_width="match_parent"
|
||||||
style="@style/Signal.Text.Headline.Registration"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginTop="40dp"
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
android:text="@string/activity_display_name_title"
|
android:textSize="@dimen/small_font_size"
|
||||||
android:textAlignment="center" />
|
android:textColor="@color/text"
|
||||||
|
android:text="This will be your name when you use Session." />
|
||||||
|
|
||||||
<TextView
|
<EditText
|
||||||
android:id="@+id/subtitleTextView"
|
style="@style/SmallSessionEditText"
|
||||||
style="@style/Signal.Text.Body"
|
android:id="@+id/displayNameEditText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
android:text="@string/activity_display_name_subtitle"
|
android:layout_marginTop="12dp"
|
||||||
android:textAlignment="center" />
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:hint="Enter a display name" />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
<View
|
||||||
android:id="@+id/nameEditText"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_weight="1"/>
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
|
||||||
app:labeledEditText_label="@string/activity_display_name_name_edit_text_label"/>
|
|
||||||
|
|
||||||
<com.dd.CircularProgressButton
|
<Button
|
||||||
android:id="@+id/nextButton"
|
style="@style/MediumProminentFilledButton"
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/registerButton"
|
||||||
android:layout_height="50dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginTop="20dp"
|
android:layout_height="@dimen/medium_button_height"
|
||||||
android:layout_marginBottom="40dp"
|
android:layout_marginLeft="@dimen/massive_spacing"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_marginRight="@dimen/massive_spacing"
|
||||||
android:background="@color/signal_primary"
|
android:layout_marginBottom="@dimen/small_spacing"
|
||||||
android:textColor="@color/white"
|
android:text="Continue" />
|
||||||
app:cpb_colorIndicator="@color/white"
|
|
||||||
app:cpb_colorProgress="@color/textsecure_primary"
|
|
||||||
app:cpb_cornerRadius="4dp"
|
|
||||||
app:cpb_selectorIdle="@drawable/progress_button_state"
|
|
||||||
app:cpb_textIdle="@string/activity_display_name_button_title" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@drawable/default_session_background"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<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: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="Pick your display name" />
|
|
||||||
|
|
||||||
<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="This will be your name when you use Session." />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
style="@style/SmallSessionEditText"
|
|
||||||
android:id="@+id/displayNameEditText"
|
|
||||||
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:hint="Enter a display name" />
|
|
||||||
|
|
||||||
<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/small_spacing"
|
|
||||||
android:text="Continue" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -1,158 +1,77 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".loki.SeedActivity">
|
android:background="@drawable/default_session_background"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView
|
||||||
|
android:id="@+id/seedReminderView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="32dp"
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
android:layout_marginEnd="32dp"
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
android:orientation="vertical"
|
android:textSize="@dimen/large_font_size"
|
||||||
android:animateLayoutChanges="true">
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="Meet your recovery phrase" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/titleTextView"
|
android:layout_width="match_parent"
|
||||||
style="@style/Signal.Text.Headline.Registration"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="10dp"
|
||||||
android:layout_marginTop="40dp"
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
android:text="@string/activity_key_pair_title"
|
android:textSize="@dimen/medium_font_size"
|
||||||
android:textAlignment="center" />
|
android:textColor="@color/text"
|
||||||
|
android:text="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. To restore your Session ID, launch Session and tap Continue your Session." />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/seedExplanationTextView1"
|
style="@style/SessionIDTextView"
|
||||||
style="@style/Signal.Text.Body"
|
android:id="@+id/seedTextView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
android:text="@string/activity_key_pair_seed_explanation_1"
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
android:textStyle="bold"
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
android:textAlignment="center" />
|
android:gravity="center"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/mnemonicTextView"
|
android:id="@+id/revealButton"
|
||||||
style="@style/Signal.Text.Body"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginTop="16dp"
|
android:textAlignment="center"
|
||||||
android:alpha="0.8"
|
android:textSize="@dimen/medium_font_size"
|
||||||
android:textStyle="italic"
|
android:textColor="@color/text"
|
||||||
android:textAlignment="center"
|
android:alpha="0.6"
|
||||||
tools:text="quick brown fox jump lazy dog" />
|
android:text="Hold to reveal" />
|
||||||
|
|
||||||
<Button
|
<View
|
||||||
android:id="@+id/copyButton"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="0dp"
|
||||||
android:layout_height="50dp"
|
android:layout_weight="1"/>
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
android:text="@string/activity_key_pair_copy_button_title"
|
|
||||||
android:elevation="0dp"
|
|
||||||
android:stateListAnimator="@null" />
|
|
||||||
|
|
||||||
<TextView
|
<Button
|
||||||
android:id="@+id/seedExplanationTextView2"
|
style="@style/MediumProminentOutlineButton"
|
||||||
style="@style/Signal.Text.Body"
|
android:id="@+id/copyButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="196dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/medium_button_height"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginBottom="@dimen/medium_spacing"
|
||||||
android:visibility="gone"
|
android:text="Copy" />
|
||||||
android:text="@string/activity_key_pair_seed_explanation_2"
|
|
||||||
android:textAlignment="center" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
</LinearLayout>
|
||||||
android:id="@+id/mnemonicEditText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
|
||||||
app:labeledEditText_label="@string/activity_key_pair_mnemonic_edit_text_label"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/linkExplanationTextView"
|
|
||||||
style="@style/Signal.Text.Body"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:text="@string/activity_key_pair_seed_explanation_3"
|
|
||||||
android:textAlignment="center" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
|
||||||
android:id="@+id/publicKeyEditText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
|
||||||
app:labeledEditText_label="@string/activity_key_pair_public_key_edit_text_label"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/scanQRButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:elevation="0dp"
|
|
||||||
android:stateListAnimator="@null"
|
|
||||||
android:text="@string/fragment_scan_qr_code_title"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/toggleRestoreModeButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
android:text="@string/activity_key_pair_toggle_mode_button_title_1"
|
|
||||||
android:elevation="0dp"
|
|
||||||
android:stateListAnimator="@null" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/toggleRegisterModeButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
android:text="@string/activity_key_pair_toggle_mode_button_title_2"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:elevation="0dp"
|
|
||||||
android:stateListAnimator="@null" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/toggleLinkModeButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
android:text="@string/activity_key_pair_toggle_mode_button_title_3"
|
|
||||||
android:elevation="0dp"
|
|
||||||
android:stateListAnimator="@null" />
|
|
||||||
|
|
||||||
<com.dd.CircularProgressButton
|
|
||||||
android:id="@+id/mainButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:layout_marginBottom="40dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:background="@color/signal_primary"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
app:cpb_colorIndicator="@color/white"
|
|
||||||
app:cpb_colorProgress="@color/textsecure_primary"
|
|
||||||
app:cpb_cornerRadius="4dp"
|
|
||||||
app:cpb_selectorIdle="@drawable/progress_button_state"
|
|
||||||
app:cpb_textIdle="@string/activity_key_pair_main_button_title_1" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,77 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@drawable/default_session_background"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView
|
|
||||||
android:id="@+id/seedReminderView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<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: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="Meet your recovery phrase" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_marginRight="@dimen/very_large_spacing"
|
|
||||||
android:textSize="@dimen/medium_font_size"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:text="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. To restore your Session ID, launch Session and tap Continue your Session." />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/SessionIDTextView"
|
|
||||||
android:id="@+id/seedTextView"
|
|
||||||
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:gravity="center"
|
|
||||||
android:textSize="@dimen/medium_font_size"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/revealButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="@dimen/medium_font_size"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:alpha="0.6"
|
|
||||||
android:text="Hold to reveal" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/MediumProminentOutlineButton"
|
|
||||||
android:id="@+id/copyButton"
|
|
||||||
android:layout_width="196dp"
|
|
||||||
android:layout_height="@dimen/medium_button_height"
|
|
||||||
android:layout_marginBottom="@dimen/medium_spacing"
|
|
||||||
android:text="Copy" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -107,7 +107,7 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.loki.redesign.views.SeparatorView
|
<org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView
|
||||||
android:id="@+id/separatorView"
|
android:id="@+id/separatorView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
android:id="@+id/moderator_icon_image_view"
|
android:id="@+id/moderator_icon_image_view"
|
||||||
android:layout_width="20dp"
|
android:layout_width="20dp"
|
||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:src="@drawable/icon_crown"
|
android:src="@drawable/ic_crown"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentBottom="true" />
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." />
|
android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." />
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.loki.redesign.views.SeparatorView
|
<org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView
|
||||||
android:id="@+id/separatorView"
|
android:id="@+id/separatorView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
android:id="@+id/moderatorIconImageView"
|
android:id="@+id/moderatorIconImageView"
|
||||||
android:layout_width="16dp"
|
android:layout_width="16dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="16dp"
|
||||||
android:src="@drawable/icon_crown"
|
android:src="@drawable/ic_crown"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentBottom="true" />
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
|
@ -1618,13 +1618,13 @@
|
|||||||
<!-- Friend request view -->
|
<!-- Friend request view -->
|
||||||
<string name="view_friend_request_accept_button_title">Accept</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_reject_button_title">Decline</string>
|
||||||
<string name="view_friend_request_incoming_pending_message">%1$s sent you a message request</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 message 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 message 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 message request has expired</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 message request</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 message 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 message request to %1$s has expired</string>
|
<string name="view_friend_request_outgoing_expired_message">Your session request to %1$s has expired</string>
|
||||||
<!-- Conversation activity -->
|
<!-- Conversation activity -->
|
||||||
<string name="activity_conversation_pending_friend_request_hint">Pending Friend Request…</string>
|
<string name="activity_conversation_pending_friend_request_hint">Pending Friend Request…</string>
|
||||||
<string name="activity_conversation_default_hint">New Message</string>
|
<string name="activity_conversation_default_hint">New Message</string>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<domain includeSubdomains="true">storage.seed1.loki.network</domain>
|
<domain includeSubdomains="true">storage.seed1.loki.network</domain>
|
||||||
<domain includeSubdomains="true">storage.seed2.loki.network</domain>
|
<domain includeSubdomains="true">storage.seed2.loki.network</domain>
|
||||||
<domain includeSubdomains="true">public.loki.foundation:22023</domain>
|
<domain includeSubdomains="true">public.loki.foundation:22023</domain>
|
||||||
|
<domain includeSubdomains="true">file-dev.lokinet.org</domain>
|
||||||
<domain includeSubdomains="true">127.0.0.1</domain>
|
<domain includeSubdomains="true">127.0.0.1</domain>
|
||||||
</domain-config>
|
</domain-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
@ -96,14 +96,13 @@ import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
|
|||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
|
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiLongPoller;
|
import org.whispersystems.signalservice.loki.api.LokiLongPoller;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
|
import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
|
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
|
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
|
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiRSSFeed;
|
import org.whispersystems.signalservice.loki.api.LokiRSSFeed;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
|
||||||
|
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -115,7 +114,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import dagger.ObjectGraph;
|
import dagger.ObjectGraph;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import network.loki.messenger.BuildConfig;
|
import network.loki.messenger.BuildConfig;
|
||||||
import okhttp3.Cache;
|
|
||||||
|
|
||||||
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
|
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
|
||||||
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
||||||
@ -184,18 +182,19 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||||
// Loki - Set up P2P API if needed
|
// Loki - Set up P2P API if needed
|
||||||
setUpP2PAPI();
|
setUpP2PAPI();
|
||||||
// Loki - Set the cache
|
|
||||||
LokiDotNetAPI.setCache(new Cache(this.getCacheDir(), OK_HTTP_CACHE_SIZE));
|
|
||||||
// Loki - Update device mappings
|
// Loki - Update device mappings
|
||||||
if (setUpStorageAPIIfNeeded()) {
|
if (setUpStorageAPIIfNeeded()) {
|
||||||
LokiStorageAPI.Companion.getShared().updateUserDeviceMappings();
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (TextSecurePreferences.needsRevocationCheck(this)) {
|
if (userHexEncodedPublicKey != null) {
|
||||||
checkNeedsRevocation();
|
LokiFileServerAPI.Companion.getShared().getDeviceLinks(userHexEncodedPublicKey, true);
|
||||||
|
if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) {
|
||||||
|
MultiDeviceUtilities.checkIsRevokedSlaveDevice(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Loki - Set up public chat manager
|
// Loki - Set up public chat manager
|
||||||
lokiPublicChatManager = new LokiPublicChatManager(this);
|
lokiPublicChatManager = new LokiPublicChatManager(this);
|
||||||
updatePublicChatProfileAvatarIfNeeded();
|
updatePublicChatProfilePictureIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -206,6 +205,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
KeyCachingService.onAppForegrounded(this);
|
KeyCachingService.onAppForegrounded(this);
|
||||||
// Loki - Start long polling if needed
|
// Loki - Start long polling if needed
|
||||||
startLongPollingIfNeeded();
|
startLongPollingIfNeeded();
|
||||||
|
// Loki - Start open group polling if needed
|
||||||
lokiPublicChatManager.startPollersIfNeeded();
|
lokiPublicChatManager.startPollersIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +466,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
boolean isDebugMode = BuildConfig.DEBUG;
|
boolean isDebugMode = BuildConfig.DEBUG;
|
||||||
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
|
||||||
LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
|
LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
|
||||||
LokiStorageAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
|
LokiFileServerAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -534,7 +534,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
|
|
||||||
public void createRSSFeedsIfNeeded() {
|
public void createRSSFeedsIfNeeded() {
|
||||||
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
|
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
|
||||||
feeds.add(lokiNewsFeed());
|
// feeds.add(lokiNewsFeed());
|
||||||
feeds.add(lokiMessengerUpdatesFeed());
|
feeds.add(lokiMessengerUpdatesFeed());
|
||||||
for (LokiRSSFeed feed : feeds) {
|
for (LokiRSSFeed feed : feeds) {
|
||||||
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
|
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
|
||||||
@ -590,7 +590,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
|
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePublicChatProfileAvatarIfNeeded() {
|
public void updatePublicChatProfilePictureIfNeeded() {
|
||||||
AsyncTask.execute(() -> {
|
AsyncTask.execute(() -> {
|
||||||
LokiPublicChatAPI publicChatAPI = null;
|
LokiPublicChatAPI publicChatAPI = null;
|
||||||
try {
|
try {
|
||||||
@ -616,11 +616,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
|
|
||||||
public void checkNeedsRevocation() {
|
|
||||||
MultiDeviceUtilities.checkForRevocation(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkNeedsDatabaseReset() {
|
public void checkNeedsDatabaseReset() {
|
||||||
if (TextSecurePreferences.resetDatabase(this)) {
|
if (TextSecurePreferences.resetDatabase(this)) {
|
||||||
@ -646,4 +641,5 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
this.startActivity(mainIntent);
|
this.startActivity(mainIntent);
|
||||||
Runtime.getRuntime().exit(0);
|
Runtime.getRuntime().exit(0);
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ import org.whispersystems.signalservice.api.crypto.ProfileCipher;
|
|||||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
|
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -407,7 +407,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
|||||||
//Loki - Upload the profile photo here
|
//Loki - Upload the profile photo here
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
Log.d("Loki", "Start uploading profile photo");
|
Log.d("Loki", "Start uploading profile photo");
|
||||||
LokiStorageAPI storageAPI = LokiStorageAPI.shared;
|
LokiFileServerAPI storageAPI = LokiFileServerAPI.shared;
|
||||||
LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar);
|
LokiDotNetAPI.UploadResult result = storageAPI.uploadProfilePicture(storageAPI.getServer(), profileKey, avatar);
|
||||||
Log.d("Loki", "Profile photo uploaded, the url is " + result.getUrl());
|
Log.d("Loki", "Profile photo uploaded, the url is " + result.getUrl());
|
||||||
TextSecurePreferences.setProfileAvatarUrl(context, result.getUrl());
|
TextSecurePreferences.setProfileAvatarUrl(context, result.getUrl());
|
||||||
@ -422,7 +422,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
|||||||
ProfileKeyUtil.setEncodedProfileKey(context, newProfileKey);
|
ProfileKeyUtil.setEncodedProfileKey(context, newProfileKey);
|
||||||
|
|
||||||
// Update profile key on the public chat server
|
// Update profile key on the public chat server
|
||||||
ApplicationContext.getInstance(context).updatePublicChatProfileAvatarIfNeeded();
|
ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.d("Loki", "Failed to upload profile photo: " + e);
|
Log.d("Loki", "Failed to upload profile photo: " + e);
|
||||||
return false;
|
return false;
|
||||||
|
@ -190,7 +190,7 @@ public class DeviceListFragment extends ListFragment
|
|||||||
private void updateAddDeviceButtonVisibility() {
|
private void updateAddDeviceButtonVisibility() {
|
||||||
if (addDeviceButton != null) {
|
if (addDeviceButton != null) {
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||||
boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getPairingAuthorisations(userHexEncodedPublicKey).isEmpty();
|
boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getDeviceLinks(userHexEncodedPublicKey).isEmpty();
|
||||||
addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE);
|
addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,13 @@ import org.thoughtcrime.securesms.database.Address;
|
|||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
@ -83,7 +82,7 @@ public class TypingStatusSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendTyping(long threadId, boolean typingStarted) {
|
private void sendTyping(long threadId, boolean typingStarted) {
|
||||||
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
LokiFileServerAPI storageAPI = LokiFileServerAPI.Companion.getShared();
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
|
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
|
||||||
|
|
||||||
@ -91,7 +90,7 @@ public class TypingStatusSender {
|
|||||||
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted));
|
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LokiStorageAPI.shared.getAllDevicePublicKeys(recipient.getAddress().serialize()).success(devices -> {
|
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(recipient.getAddress().serialize()).success(devices -> {
|
||||||
for (String device : devices) {
|
for (String device : devices) {
|
||||||
Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false);
|
Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false);
|
||||||
long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient);
|
long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient);
|
||||||
|
@ -232,10 +232,10 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
|||||||
import org.thoughtcrime.securesms.util.views.Stub;
|
import org.thoughtcrime.securesms.util.views.Stub;
|
||||||
import org.whispersystems.libsignal.InvalidMessageException;
|
import org.whispersystems.libsignal.InvalidMessageException;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.loki.api.DeviceLink;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
|
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation;
|
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
|
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
||||||
import org.whispersystems.signalservice.loki.messaging.Mention;
|
import org.whispersystems.signalservice.loki.messaging.Mention;
|
||||||
@ -331,7 +331,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
private MenuItem searchViewItem;
|
private MenuItem searchViewItem;
|
||||||
private ProgressBar messageStatusProgressBar;
|
private ProgressBar messageStatusProgressBar;
|
||||||
private ImageView muteIndicatorImageView;
|
private ImageView muteIndicatorImageView;
|
||||||
private TextView actionBarSubtitleTextView;
|
private TextView subtitleTextView;
|
||||||
|
|
||||||
private AttachmentTypeSelector attachmentTypeSelector;
|
private AttachmentTypeSelector attachmentTypeSelector;
|
||||||
private AttachmentManager attachmentManager;
|
private AttachmentManager attachmentManager;
|
||||||
@ -362,6 +362,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme();
|
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||||
|
|
||||||
|
// Message Status Bar
|
||||||
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
|
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
|
||||||
private String messageStatus = null;
|
private String messageStatus = null;
|
||||||
|
|
||||||
@ -404,6 +405,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
registerMessageStatusObserver("sendingMessage");
|
registerMessageStatusObserver("sendingMessage");
|
||||||
registerMessageStatusObserver("messageSent");
|
registerMessageStatusObserver("messageSent");
|
||||||
registerMessageStatusObserver("messageFailed");
|
registerMessageStatusObserver("messageFailed");
|
||||||
|
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Toast.makeText(ConversationActivity.this, "Your clock is out of sync with the service node network.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
broadcastReceivers.add(broadcastReceiver);
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("clockOutOfSync"));
|
||||||
|
|
||||||
initializeReceivers();
|
initializeReceivers();
|
||||||
initializeActionBar();
|
initializeActionBar();
|
||||||
@ -543,7 +553,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
initializeIdentityRecords();
|
initializeIdentityRecords();
|
||||||
composeText.setTransport(sendButton.getSelectedTransport());
|
composeText.setTransport(sendButton.getSelectedTransport());
|
||||||
|
|
||||||
updateTitleTextView(glideRequests, recipient);
|
updateTitleTextView(recipient);
|
||||||
updateSubtitleTextView();
|
updateSubtitleTextView();
|
||||||
setActionBarColor(recipient.getColor());
|
setActionBarColor(recipient.getColor());
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||||
@ -636,7 +646,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
case GROUP_EDIT:
|
case GROUP_EDIT:
|
||||||
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
|
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
|
||||||
recipient.addListener(this);
|
recipient.addListener(this);
|
||||||
updateTitleTextView(glideRequests, recipient);
|
updateTitleTextView(recipient);
|
||||||
updateSubtitleTextView();
|
updateSubtitleTextView();
|
||||||
NotificationChannels.updateContactChannelName(this, recipient);
|
NotificationChannels.updateContactChannelName(this, recipient);
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||||
@ -740,9 +750,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
MenuInflater inflater = this.getMenuInflater();
|
MenuInflater inflater = this.getMenuInflater();
|
||||||
menu.clear();
|
menu.clear();
|
||||||
|
|
||||||
boolean isLokiGroupChat = recipient.getAddress().isPublicChat() || recipient.getAddress().isRSSFeed();
|
boolean isOpenGroupOrRSSFeed = recipient.getAddress().isPublicChat() || recipient.getAddress().isRSSFeed();
|
||||||
|
|
||||||
if (isSecureText && !isLokiGroupChat) {
|
if (isSecureText && !isOpenGroupOrRSSFeed) {
|
||||||
if (recipient.getExpireMessages() > 0) {
|
if (recipient.getExpireMessages() > 0) {
|
||||||
inflater.inflate(R.menu.conversation_expiring_on, menu);
|
inflater.inflate(R.menu.conversation_expiring_on, menu);
|
||||||
|
|
||||||
@ -762,7 +772,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
if (isSecureText) inflater.inflate(R.menu.conversation_callable_secure, menu);
|
if (isSecureText) inflater.inflate(R.menu.conversation_callable_secure, menu);
|
||||||
else inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
else inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
||||||
*/
|
*/
|
||||||
} else if (isGroupConversation() && !isLokiGroupChat) {
|
} else if (isGroupConversation() && !isOpenGroupOrRSSFeed) {
|
||||||
inflater.inflate(R.menu.conversation_group_options, menu);
|
inflater.inflate(R.menu.conversation_group_options, menu);
|
||||||
|
|
||||||
if (!isPushGroupConversation()) {
|
if (!isPushGroupConversation()) {
|
||||||
@ -1679,7 +1689,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView);
|
sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView);
|
||||||
messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
|
messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
|
||||||
muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
|
muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
|
||||||
actionBarSubtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
|
subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
|
||||||
|
|
||||||
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
|
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
|
||||||
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
|
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
|
||||||
@ -1870,7 +1880,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
|
Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
|
||||||
Util.runOnMain(() -> {
|
Util.runOnMain(() -> {
|
||||||
Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
|
Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
|
||||||
updateTitleTextView(glideRequests, recipient);
|
updateTitleTextView(recipient);
|
||||||
updateSubtitleTextView();
|
updateSubtitleTextView();
|
||||||
// titleView.setVerified(identityRecords.isVerified());
|
// titleView.setVerified(identityRecords.isVerified());
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||||
@ -2290,7 +2300,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
if (threadID != this.threadId) {
|
if (threadID != this.threadId) {
|
||||||
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
|
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
|
||||||
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
|
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
|
||||||
LokiStorageAPI.shared.getAllDevicePublicKeys(threadRecipient.getAddress().serialize()).success(devices -> {
|
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(threadRecipient.getAddress().serialize()).success(devices -> {
|
||||||
// We should update our input if this thread is a part of the other threads device
|
// We should update our input if this thread is a part of the other threads device
|
||||||
if (devices.contains(recipient.getAddress().serialize())) {
|
if (devices.contains(recipient.getAddress().serialize())) {
|
||||||
this.updateInputPanel();
|
this.updateInputPanel();
|
||||||
@ -2313,33 +2323,29 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
private void updateInputPanel() {
|
private void updateInputPanel() {
|
||||||
/*
|
/*
|
||||||
isFriendsWithAnyDevice caches whether we are friends with any of the other users device.
|
isFriendsWithAnyDevice reflects whether we are friends with any of the other user's devices.
|
||||||
|
|
||||||
This stops the case where the input panel disables and enables rapidly.
|
This fixes the case where the input panel disables and enables rapidly, which can occur when we are
|
||||||
- This can occur when we are not friends with the current thread BUT multi-device tells us that we are friends with another one of their devices.
|
not friends with the current thread BUT multi device tells us that we are friends with another one of their devices.
|
||||||
*/
|
*/
|
||||||
if (recipient.isGroupRecipient() || isNoteToSelf() || isFriendsWithAnyDevice) {
|
if (recipient.isGroupRecipient() || isNoteToSelf() || isFriendsWithAnyDevice) { setInputPanelEnabled(true); return; }
|
||||||
setInputPanelEnabled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It could take a while before our promise resolves, so we assume the best case
|
// Disable the input panel if a friend request is pending
|
||||||
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
|
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
|
||||||
boolean isPending = friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED;
|
boolean isPending = friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED;
|
||||||
setInputPanelEnabled(!isPending);
|
setInputPanelEnabled(!isPending);
|
||||||
|
|
||||||
// We should always have the input panel enabled if we are friends with the current user
|
// Always enable the input panel if we are friends with the current user
|
||||||
isFriendsWithAnyDevice = friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS;
|
isFriendsWithAnyDevice = (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS);
|
||||||
|
|
||||||
// Multi-device input logic
|
|
||||||
if (!isFriendsWithAnyDevice) {
|
if (!isFriendsWithAnyDevice) {
|
||||||
// We should enable the input if we don't have any pending friend requests OR we are friends with a linked device
|
// Enable the input panel if we don't have any pending friend requests OR we are friends with one of the user's linked devices
|
||||||
MultiDeviceUtilities.hasPendingFriendRequestWithAnyLinkedDevice(this, recipient).success(hasPendingRequests -> {
|
MultiDeviceUtilities.hasPendingFriendRequestWithAnyLinkedDevice(this, recipient).success( hasPendingRequests -> {
|
||||||
if (!hasPendingRequests) {
|
if (!hasPendingRequests) {
|
||||||
setInputPanelEnabled(true);
|
setInputPanelEnabled(true);
|
||||||
} else {
|
} else {
|
||||||
MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient).success(isFriends -> {
|
MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient).success( isFriends -> {
|
||||||
// If we are friend with any of the other devices then we want to make sure the input panel is always enabled for the duration of this conversation
|
// Enable the input panel if we're friends with any of the user's devices
|
||||||
isFriendsWithAnyDevice = isFriends;
|
isFriendsWithAnyDevice = isFriends;
|
||||||
setInputPanelEnabled(isFriends);
|
setInputPanelEnabled(isFriends);
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
@ -2353,7 +2359,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
private void setInputPanelEnabled(boolean enabled) {
|
private void setInputPanelEnabled(boolean enabled) {
|
||||||
Util.runOnMain(() -> {
|
Util.runOnMain(() -> {
|
||||||
updateToggleButtonState();
|
updateToggleButtonState();
|
||||||
String hint = enabled ? "Message" : "Pending message request";
|
String hint = enabled ? "Message" : "Pending session request";
|
||||||
inputPanel.setHint(hint);
|
inputPanel.setHint(hint);
|
||||||
inputPanel.setEnabled(enabled);
|
inputPanel.setEnabled(enabled);
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
@ -2407,13 +2413,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
sendTextMessage(forceSms, expiresIn, subscriptionId, initiating);
|
sendTextMessage(forceSms, expiresIn, subscriptionId, initiating);
|
||||||
}
|
}
|
||||||
} catch (RecipientFormattingException ex) {
|
} catch (RecipientFormattingException ex) {
|
||||||
Toast.makeText(ConversationActivity.this,
|
|
||||||
R.string.ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
Log.w(TAG, ex);
|
Log.w(TAG, ex);
|
||||||
} catch (InvalidMessageException ex) {
|
} catch (InvalidMessageException ex) {
|
||||||
// Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_message_is_empty_exclamation,
|
|
||||||
// Toast.LENGTH_SHORT).show();
|
|
||||||
Log.w(TAG, ex);
|
Log.w(TAG, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2565,7 +2566,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateToggleButtonState() {
|
private void updateToggleButtonState() {
|
||||||
// Don't allow attachments if we're not friends with any device
|
// Don't allow attachments if we're not friends with any of the user's devices
|
||||||
if (!isNoteToSelf() && !recipient.isGroupRecipient() && !isFriendsWithAnyDevice) {
|
if (!isNoteToSelf() && !recipient.isGroupRecipient() && !isFriendsWithAnyDevice) {
|
||||||
buttonToggle.display(sendButton);
|
buttonToggle.display(sendButton);
|
||||||
quickAttachmentToggle.hide();
|
quickAttachmentToggle.hide();
|
||||||
@ -3160,13 +3161,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
// region Loki
|
// region Loki
|
||||||
private void updateTitleTextView(GlideRequests glide, Recipient recipient) {
|
private void updateTitleTextView(Recipient recipient) {
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
List<PairingAuthorisation> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getPairingAuthorisations(userHexEncodedPublicKey);
|
Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userHexEncodedPublicKey);
|
||||||
HashSet<String> userLinkedDeviceHexEncodedPublicKeys = new HashSet<>();
|
HashSet<String> userLinkedDeviceHexEncodedPublicKeys = new HashSet<>();
|
||||||
for (PairingAuthorisation deviceLink : deviceLinks) {
|
for (DeviceLink deviceLink : deviceLinks) {
|
||||||
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getPrimaryDevicePublicKey().toLowerCase());
|
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getMasterHexEncodedPublicKey().toLowerCase());
|
||||||
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getSecondaryDevicePublicKey().toLowerCase());
|
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getSlaveHexEncodedPublicKey().toLowerCase());
|
||||||
}
|
}
|
||||||
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase());
|
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase());
|
||||||
if (recipient == null) {
|
if (recipient == null) {
|
||||||
@ -3174,45 +3175,46 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
} else if (userLinkedDeviceHexEncodedPublicKeys.contains(recipient.getAddress().toString().toLowerCase())) {
|
} else if (userLinkedDeviceHexEncodedPublicKeys.contains(recipient.getAddress().toString().toLowerCase())) {
|
||||||
titleTextView.setText("Note to Self");
|
titleTextView.setText("Note to Self");
|
||||||
} else {
|
} else {
|
||||||
titleTextView.setText((recipient.getName() == null || recipient.getName().isEmpty()) ? recipient.getAddress().toString() : recipient.getName());
|
boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty());
|
||||||
|
titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSubtitleTextView() {
|
private void updateSubtitleTextView() {
|
||||||
muteIndicatorImageView.setVisibility(View.GONE);
|
muteIndicatorImageView.setVisibility(View.GONE);
|
||||||
actionBarSubtitleTextView.setVisibility(View.VISIBLE);
|
subtitleTextView.setVisibility(View.VISIBLE);
|
||||||
if (messageStatus != null) {
|
if (messageStatus != null) {
|
||||||
switch (messageStatus) {
|
switch (messageStatus) {
|
||||||
case "calculatingPoW": actionBarSubtitleTextView.setText("Encrypting message"); break;
|
case "calculatingPoW": subtitleTextView.setText("Encrypting message"); break;
|
||||||
case "contactingNetwork": actionBarSubtitleTextView.setText("Tracing a path"); break;
|
case "contactingNetwork": subtitleTextView.setText("Tracing a path"); break;
|
||||||
case "sendingMessage": actionBarSubtitleTextView.setText("Sending message"); break;
|
case "sendingMessage": subtitleTextView.setText("Sending message"); break;
|
||||||
case "messageSent": actionBarSubtitleTextView.setText("Message sent securely"); break;
|
case "messageSent": subtitleTextView.setText("Message sent securely"); break;
|
||||||
case "messageFailed": actionBarSubtitleTextView.setText("Message failed to send"); break;
|
case "messageFailed": subtitleTextView.setText("Message failed to send"); break;
|
||||||
}
|
}
|
||||||
} else if (recipient.isMuted()) {
|
} else if (recipient.isMuted()) {
|
||||||
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
||||||
actionBarSubtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
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")) {
|
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
||||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||||
if (publicChat != null) {
|
if (publicChat != null) {
|
||||||
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
||||||
if (userCount == null) { userCount = 0; }
|
if (userCount == null) { userCount = 0; }
|
||||||
if (userCount >= 200) {
|
if (userCount >= 200) {
|
||||||
actionBarSubtitleTextView.setText("200+ members");
|
subtitleTextView.setText("200+ members");
|
||||||
} else {
|
} else {
|
||||||
actionBarSubtitleTextView.setText(userCount + " members");
|
subtitleTextView.setText(userCount + " members");
|
||||||
}
|
}
|
||||||
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
||||||
actionBarSubtitleTextView.setText(recipient.getAddress().toString());
|
subtitleTextView.setText(recipient.getAddress().toString());
|
||||||
} else {
|
} else {
|
||||||
actionBarSubtitleTextView.setVisibility(View.GONE);
|
subtitleTextView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
||||||
actionBarSubtitleTextView.setText(recipient.getAddress().toString());
|
subtitleTextView.setText(recipient.getAddress().toString());
|
||||||
} else {
|
} else {
|
||||||
actionBarSubtitleTextView.setVisibility(View.GONE);
|
subtitleTextView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((actionBarSubtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size));
|
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((subtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMessageStatusProgressAnimatedIfPossible(int progress) {
|
private void setMessageStatusProgressAnimatedIfPossible(int progress) {
|
||||||
@ -3284,18 +3286,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
|
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
|
||||||
// Send the accept to the original friend request thread id
|
// Send the accept to the original friend request thread ID
|
||||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
|
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
|
||||||
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
|
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
|
||||||
long threadId = originalThreadID < 0 ? this.threadId : originalThreadID;
|
long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
|
||||||
|
Recipient contact = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
|
||||||
Recipient contact = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId);
|
|
||||||
Address address = contact.getAddress();
|
Address address = contact.getAddress();
|
||||||
String contactPubKey = address.serialize();
|
String contactHexEncodedPublicKey = address.serialize();
|
||||||
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.FRIENDS);
|
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
||||||
lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
|
lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
|
||||||
DatabaseFactory.getRecipientDatabase(this).setProfileSharing(contact, true);
|
DatabaseFactory.getRecipientDatabase(this).setProfileSharing(contact, true);
|
||||||
MessageSender.sendBackgroundMessageToAllDevices(this, contactPubKey);
|
MessageSender.sendBackgroundMessageToAllDevices(this, contactHexEncodedPublicKey);
|
||||||
MessageSender.syncContact(this, address);
|
MessageSender.syncContact(this, address);
|
||||||
updateInputPanel();
|
updateInputPanel();
|
||||||
}
|
}
|
||||||
@ -3304,10 +3305,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
|
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
|
||||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
|
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
|
||||||
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
|
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
|
||||||
long threadId = originalThreadID < 0 ? this.threadId : originalThreadID;
|
long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
|
||||||
|
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.NONE);
|
||||||
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.NONE);
|
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID).getAddress().toString();
|
||||||
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId).getAddress().toString();
|
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID);
|
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID);
|
||||||
updateInputPanel();
|
updateInputPanel();
|
||||||
}
|
}
|
||||||
@ -3315,20 +3315,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
public boolean isNoteToSelf() {
|
public boolean isNoteToSelf() {
|
||||||
return TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize());
|
return TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize());
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
|
|
||||||
public void restoreSession() {
|
public void restoreSession() {
|
||||||
// Loki - User clicked restore session
|
|
||||||
if (recipient.isGroupRecipient()) { return; }
|
if (recipient.isGroupRecipient()) { return; }
|
||||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(this);
|
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(this);
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(this);
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(this);
|
||||||
Set<String> devices = lokiThreadDatabase.getSessionRestoreDevices(threadId);
|
Set<String> devices = lokiThreadDatabase.getSessionRestoreDevices(threadId);
|
||||||
for (String device : devices) { MessageSender.sendRestoreSessionMessage(this, device); }
|
for (String device : devices) { MessageSender.sendRestoreSessionMessage(this, device); }
|
||||||
long messageId = smsDatabase.insertMessageOutbox(threadId, new OutgoingTextMessage(recipient,"", 0, 0), false, System.currentTimeMillis(), null);
|
long messageID = smsDatabase.insertMessageOutbox(threadId, new OutgoingTextMessage(recipient,"", 0, 0), false, System.currentTimeMillis(), null);
|
||||||
if (messageId > -1) {
|
if (messageID > -1) {
|
||||||
smsDatabase.markAsLokiSessionRestoreSent(messageId);
|
smsDatabase.markAsLokiSessionRestoreSent(messageID);
|
||||||
}
|
}
|
||||||
lokiThreadDatabase.removeAllSessionRestoreDevices(threadId);
|
lokiThreadDatabase.removeAllSessionRestoreDevices(threadId);
|
||||||
updateSessionRestoreBanner();
|
updateSessionRestoreBanner();
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||||
import org.whispersystems.libsignal.state.SessionRecord;
|
import org.whispersystems.libsignal.state.SessionRecord;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiSessionDatabaseProtocol;
|
import org.whispersystems.libsignal.state.SessionStore;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TextSecureSessionStore implements LokiSessionDatabaseProtocol {
|
public class TextSecureSessionStore implements SessionStore {
|
||||||
|
|
||||||
private static final String TAG = TextSecureSessionStore.class.getSimpleName();
|
private static final String TAG = TextSecureSessionStore.class.getSimpleName();
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateGroupChatAuthTokenTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand());
|
||||||
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
|
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
|
||||||
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
|
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
|
||||||
@ -518,7 +518,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < lokiV3) {
|
if (oldVersion < lokiV3) {
|
||||||
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand());
|
||||||
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
|
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
|
||||||
|
|
||||||
db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT");
|
db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT");
|
||||||
|
@ -11,13 +11,14 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities;
|
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities;
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader;
|
import org.thoughtcrime.securesms.util.AsyncLoader;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
|
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class DeviceListLoader extends AsyncLoader<List<Device>> {
|
public class DeviceListLoader extends AsyncLoader<List<Device>> {
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ public class DeviceListLoader extends AsyncLoader<List<Device>> {
|
|||||||
public List<Device> loadInBackground() {
|
public List<Device> loadInBackground() {
|
||||||
try {
|
try {
|
||||||
String ourPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
String ourPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
||||||
List<String> secondaryDevicePublicKeys = LokiStorageAPI.shared.getSecondaryDevicePublicKeys(ourPublicKey).get();
|
Set<String> secondaryDevicePublicKeys = LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(ourPublicKey).get();
|
||||||
List<Device> devices = Stream.of(secondaryDevicePublicKeys).map(this::mapToDevice).toList();
|
List<Device> devices = Stream.of(secondaryDevicePublicKeys).map(this::mapToDevice).toList();
|
||||||
Collections.sort(devices, new DeviceComparator());
|
Collections.sort(devices, new DeviceComparator());
|
||||||
return devices;
|
return devices;
|
||||||
|
@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.ApplicationContext;
|
|||||||
import org.thoughtcrime.securesms.CreateProfileActivity;
|
import org.thoughtcrime.securesms.CreateProfileActivity;
|
||||||
import org.thoughtcrime.securesms.DeviceListFragment;
|
import org.thoughtcrime.securesms.DeviceListFragment;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||||
import org.thoughtcrime.securesms.gcm.FcmService;
|
import org.thoughtcrime.securesms.gcm.FcmService;
|
||||||
@ -48,6 +47,7 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation;
|
||||||
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
|
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
|
||||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||||
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
|
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
|
||||||
@ -160,7 +160,7 @@ public class SignalCommunicationModule {
|
|||||||
DatabaseFactory.getLokiThreadDatabase(context),
|
DatabaseFactory.getLokiThreadDatabase(context),
|
||||||
DatabaseFactory.getLokiMessageDatabase(context),
|
DatabaseFactory.getLokiMessageDatabase(context),
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
|
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
|
||||||
new TextSecureSessionStore(context),
|
new LokiSessionResetImplementation(context),
|
||||||
DatabaseFactory.getLokiUserDatabase(context),
|
DatabaseFactory.getLokiUserDatabase(context),
|
||||||
((ApplicationContext)context.getApplicationContext()).broadcaster);
|
((ApplicationContext)context.getApplicationContext()).broadcaster);
|
||||||
} else {
|
} else {
|
||||||
|
@ -36,7 +36,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
|||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -318,7 +318,7 @@ public class GroupMessageProcessor {
|
|||||||
try {
|
try {
|
||||||
String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey)
|
String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey)
|
||||||
? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
|
? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
|
||||||
: PromiseUtil.timeout(LokiStorageAPI.shared.getPrimaryDevicePublicKey(hexEncodedPublicKey), 5000).get();
|
: PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
|
||||||
return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey;
|
return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return hexEncodedPublicKey;
|
return hexEncodedPublicKey;
|
||||||
@ -329,7 +329,7 @@ public class GroupMessageProcessor {
|
|||||||
String ourNumber = TextSecurePreferences.getLocalNumber(context);
|
String ourNumber = TextSecurePreferences.getLocalNumber(context);
|
||||||
for (String member : members) {
|
for (String member : members) {
|
||||||
// Make sure we have session with all of the members secondary devices
|
// Make sure we have session with all of the members secondary devices
|
||||||
LokiStorageAPI.shared.getAllDevicePublicKeys(member).success(devices -> {
|
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(member).success(devices -> {
|
||||||
if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
|
if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
|
||||||
for (String device : devices) {
|
for (String device : devices) {
|
||||||
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);
|
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);
|
||||||
|
@ -27,7 +27,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
|||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -68,13 +68,13 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.FriendRequestHandler;
|
import org.thoughtcrime.securesms.loki.FriendRequestHandler;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
|
|
||||||
import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
|
import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
|
import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
|
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
|
||||||
|
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
|
||||||
|
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
|
||||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
||||||
@ -101,6 +101,8 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
|
|||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol;
|
||||||
|
import org.whispersystems.libsignal.loki.LokiSessionResetStatus;
|
||||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||||
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
@ -129,15 +131,14 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
|
|||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.loki.api.DeviceLink;
|
||||||
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession;
|
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
import org.whispersystems.signalservice.loki.api.LokiAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation;
|
|
||||||
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
|
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
|
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage;
|
import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadSessionResetStatus;
|
|
||||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -270,9 +271,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
||||||
LokiPreKeyRecordDatabase lokiPreKeyRecordDatabase = DatabaseFactory.getLokiPreKeyRecordDatabase(context);
|
LokiSessionResetProtocol lokiSessionResetProtocol = new LokiSessionResetImplementation(context);
|
||||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiThreadDatabase, lokiPreKeyRecordDatabase, UnidentifiedAccessUtil.getCertificateValidator());
|
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiSessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
|
||||||
|
|
||||||
SignalServiceContent content = cipher.decrypt(envelope);
|
SignalServiceContent content = cipher.decrypt(envelope);
|
||||||
|
|
||||||
@ -282,21 +283,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (shouldIgnore(content)) {
|
if (shouldIgnore(content)) {
|
||||||
Log.i(TAG, "Ignoring message.");
|
Log.i(TAG, "Ignoring message.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle friend request acceptance if needed
|
// Loki - Handle friend request acceptance if needed
|
||||||
acceptFriendRequestIfNeeded(content);
|
if (!content.isFriendRequest() && !isGroupChatMessage(content)) {
|
||||||
|
becomeFriendsWithContactIfNeeded(content.getSender(), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Loki - Session requests
|
// Loki - Handle session request if needed
|
||||||
handleSessionRequestIfNeeded(content);
|
handleSessionRequestIfNeeded(content);
|
||||||
|
|
||||||
// Loki - Store pre key bundle
|
// Loki - Store pre key bundle if needed
|
||||||
// We shouldn't store it if it's a pairing message
|
if (!content.getDeviceLink().isPresent()) {
|
||||||
if (!content.getPairingAuthorisation().isPresent()) {
|
|
||||||
storePreKeyBundleIfNeeded(content);
|
storePreKeyBundleIfNeeded(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,35 +311,35 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
// Loki - Store the sender display name if needed
|
// Loki - Store the sender display name if needed
|
||||||
Optional<String> rawSenderDisplayName = content.senderDisplayName;
|
Optional<String> rawSenderDisplayName = content.senderDisplayName;
|
||||||
if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) {
|
if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) {
|
||||||
// If we got a name from our primary device then we set our profile name to match it
|
// If we got a name from our master device then set our display name to match
|
||||||
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
if (ourPrimaryDevice != null && content.getSender().equals(ourPrimaryDevice)) {
|
if (ourMasterDevice != null && content.getSender().equals(ourMasterDevice)) {
|
||||||
TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get());
|
TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them)
|
// If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them)
|
||||||
MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success(isOneOfOurDevice -> {
|
MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success( isOneOfOurDevices -> {
|
||||||
if (!isOneOfOurDevice) { setDisplayName(content.getSender(), rawSenderDisplayName.get()); }
|
if (!isOneOfOurDevices) { setDisplayName(content.getSender(), rawSenderDisplayName.get()); }
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.getPairingAuthorisation().isPresent()) {
|
if (content.getDeviceLink().isPresent()) {
|
||||||
handlePairingMessage(content.getPairingAuthorisation().get(), content);
|
handleDeviceLinkMessage(content.getDeviceLink().get(), content);
|
||||||
} else if (content.getDataMessage().isPresent()) {
|
} else if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
||||||
|
|
||||||
if (!content.isFriendRequest() && message.isUnpairingRequest()) {
|
if (!content.isFriendRequest() && message.isUnlinkingRequest()) {
|
||||||
// Make sure we got the request from our primary device
|
// Make sure we got the request from our master device
|
||||||
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) {
|
if (ourMasterDevice != null && ourMasterDevice.equals(content.getSender())) {
|
||||||
TextSecurePreferences.setDatabaseResetFromUnpair(context, true);
|
TextSecurePreferences.setDatabaseResetFromUnpair(context, true);
|
||||||
MultiDeviceUtilities.checkForRevocation(context);
|
MultiDeviceUtilities.checkIsRevokedSlaveDevice(context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Loki - Don't process session restore message any further
|
// Loki - Don't process session restore message any further
|
||||||
if (message.isSessionRestore() || message.isSessionRequest()) { return; }
|
if (message.isSessionRestorationRequest() || message.isSessionRequest()) { return; }
|
||||||
|
|
||||||
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
||||||
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
|
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
|
||||||
@ -362,6 +363,18 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
handleNeedsDeliveryReceipt(content, message);
|
handleNeedsDeliveryReceipt(content, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we received a friend request, but we were already friends with the user, reset the session
|
||||||
|
if (content.isFriendRequest() && !message.isGroupMessage()) {
|
||||||
|
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
||||||
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
|
long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
|
||||||
|
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
|
||||||
|
resetSession(content.getSender());
|
||||||
|
// Let our other devices know that we have reset the session
|
||||||
|
MessageSender.syncContact(context, sender.getAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Loki - Handle friend request logic if needed
|
// Loki - Handle friend request logic if needed
|
||||||
updateFriendRequestStatusIfNeeded(content, message);
|
updateFriendRequestStatusIfNeeded(content, message);
|
||||||
}
|
}
|
||||||
@ -375,7 +388,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
|
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
|
||||||
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
||||||
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
|
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
|
||||||
else if (syncMessage.getContacts().isPresent()) handleSynchronizeContactMessage(syncMessage.getContacts().get());
|
else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get());
|
||||||
else Log.w(TAG, "Contains no known sync types...");
|
else Log.w(TAG, "Contains no known sync types...");
|
||||||
} else if (content.getCallMessage().isPresent()) {
|
} else if (content.getCallMessage().isPresent()) {
|
||||||
Log.i(TAG, "Got call message...");
|
Log.i(TAG, "Got call message...");
|
||||||
@ -402,11 +415,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (envelope.isPreKeySignalMessage()) {
|
if (envelope.isPreKeySignalMessage()) {
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Handle session reset logic
|
|
||||||
if (!content.isFriendRequest()) {
|
|
||||||
cipher.handleSessionResetRequestIfNeeded(content, cipher.getSessionStatus(content));
|
|
||||||
}
|
|
||||||
} catch (ProtocolInvalidVersionException e) {
|
} catch (ProtocolInvalidVersionException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||||
@ -539,19 +547,19 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (threadId != null) {
|
if (threadId != null) {
|
||||||
resetSession(content.getSender(), threadId);
|
resetSession(content.getSender());
|
||||||
MessageNotifier.updateNotification(context, threadId);
|
MessageNotifier.updateNotification(context, threadId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetSession(String hexEncodedPublicKey, long threadId) {
|
private void resetSession(String hexEncodedPublicKey) {
|
||||||
TextSecureSessionStore sessionStore = new TextSecureSessionStore(context);
|
TextSecureSessionStore sessionStore = new TextSecureSessionStore(context);
|
||||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
||||||
|
|
||||||
Log.d("Loki", "Received a session reset request from: " + hexEncodedPublicKey + "; archiving the session.");
|
Log.d("Loki", "Received a session reset request from: " + hexEncodedPublicKey + "; archiving the session.");
|
||||||
|
|
||||||
sessionStore.archiveAllSessions(hexEncodedPublicKey);
|
sessionStore.archiveAllSessions(hexEncodedPublicKey);
|
||||||
lokiThreadDatabase.setSessionResetStatus(threadId, LokiThreadSessionResetStatus.REQUEST_RECEIVED);
|
lokiThreadDatabase.setSessionResetStatus(hexEncodedPublicKey, LokiSessionResetStatus.REQUEST_RECEIVED);
|
||||||
|
|
||||||
Log.d("Loki", "Sending a ping back to " + hexEncodedPublicKey + ".");
|
Log.d("Loki", "Sending a ping back to " + hexEncodedPublicKey + ".");
|
||||||
MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey);
|
MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey);
|
||||||
@ -593,7 +601,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
{
|
{
|
||||||
GroupMessageProcessor.process(context, content, message, false);
|
GroupMessageProcessor.process(context, content, message, false);
|
||||||
|
|
||||||
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) {
|
if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getRecipientForMessage(content, message).getExpireMessages()) {
|
||||||
handleExpirationUpdate(content, message, Optional.absent());
|
handleExpirationUpdate(content, message, Optional.absent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +627,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
Recipient recipient = getMessageDestination(content, message);
|
Recipient recipient = getRecipientForMessage(content, message);
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromSerialized(content.getSender()),
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromSerialized(content.getSender()),
|
||||||
message.getTimestamp(), -1,
|
message.getTimestamp(), -1,
|
||||||
message.getExpiresInSeconds() * 1000L, true,
|
message.getExpiresInSeconds() * 1000L, true,
|
||||||
@ -670,45 +678,44 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSynchronizeContactMessage(@NonNull ContactsMessage contactsMessage) {
|
private void handleContactSyncMessage(@NonNull ContactsMessage contactsMessage) {
|
||||||
if (contactsMessage.getContactsStream().isStream()) {
|
if (!contactsMessage.getContactsStream().isStream()) { return; }
|
||||||
Log.d("Loki", "Received contact sync message");
|
Log.d("Loki", "Received contact sync message.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream in = contactsMessage.getContactsStream().asStream().getInputStream();
|
InputStream in = contactsMessage.getContactsStream().asStream().getInputStream();
|
||||||
DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(in);
|
DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(in);
|
||||||
List<DeviceContact> devices = contactsInputStream.readAll();
|
List<DeviceContact> deviceContacts = contactsInputStream.readAll();
|
||||||
for (DeviceContact deviceContact : devices) {
|
for (DeviceContact deviceContact : deviceContacts) {
|
||||||
// Check if we have the contact as a friend and that we're not trying to sync our own device
|
// Check if we have the contact as a friend and that we're not trying to sync our own device
|
||||||
String pubKey = deviceContact.getNumber();
|
String hexEncodedPublicKey = deviceContact.getNumber();
|
||||||
Address address = Address.fromSerialized(pubKey);
|
Address address = Address.fromSerialized(hexEncodedPublicKey);
|
||||||
if (!address.isPhone() || address.toPhoneString().equals(TextSecurePreferences.getLocalNumber(context))) { continue; }
|
if (!address.isPhone() || address.toPhoneString().equals(TextSecurePreferences.getLocalNumber(context))) { continue; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If we're not friends with the contact we received or our friend request expired then we should send them a friend request
|
If we're not friends with the contact we received or our friend request expired then we should send them a friend request.
|
||||||
otherwise if we have received a friend request with from them then we should automatically accept the friend request
|
Otherwise, if we have received a friend request from them, automatically accept the friend request.
|
||||||
*/
|
*/
|
||||||
Recipient recipient = Recipient.from(context, address, false);
|
Recipient recipient = Recipient.from(context, address, false);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||||
LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
|
LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
|
||||||
if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
||||||
MessageSender.sendBackgroundFriendRequest(context, pubKey, "Please accept to enable messages to be synced across devices");
|
MessageSender.sendBackgroundFriendRequest(context, hexEncodedPublicKey, "Please accept to enable messages to be synced across devices");
|
||||||
Log.d("Loki", "Sent friend request to " + pubKey);
|
Log.d("Loki", "Sent friend request to " + hexEncodedPublicKey);
|
||||||
} else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
|
} else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
|
||||||
// Accept the incoming friend request
|
// Accept the incoming friend request
|
||||||
becomeFriendsWithContact(pubKey, false, false);
|
becomeFriendsWithContactIfNeeded(hexEncodedPublicKey, false, false);
|
||||||
// Send them an accept message back
|
// Send them an accept message back
|
||||||
MessageSender.sendBackgroundMessage(context, pubKey);
|
MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey);
|
||||||
Log.d("Loki", "Became friends with " + deviceContact.getNumber());
|
Log.d("Loki", "Became friends with " + deviceContact.getNumber());
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Handle blocked - If user is not blocked then we should do the friend request logic otherwise add them to our block list
|
|
||||||
// TODO: Handle expiration timer - Update expiration timer?
|
|
||||||
// TODO: Handle avatar - Download and set avatar?
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
Log.d("Loki", "Failed to sync contact: " + e);
|
// TODO: Handle blocked - If user is not blocked then we should do the friend request logic otherwise add them to our block list
|
||||||
|
// TODO: Handle expiration timer - Update expiration timer?
|
||||||
|
// TODO: Handle avatar - Download and set avatar?
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("Loki", "Failed to sync contact: " + e + ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,7 +758,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - If we received a sync message from our master device then we need to extract the avatar url
|
// Loki - If we received a sync message from our master device then we need to extract the profile picture url
|
||||||
if (isSenderMasterDevice) {
|
if (isSenderMasterDevice) {
|
||||||
handleProfileKey(content, message.getMessage());
|
handleProfileKey(content, message.getMessage());
|
||||||
}
|
}
|
||||||
@ -841,27 +848,27 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
@NonNull Optional<Long> messageServerIDOrNull)
|
@NonNull Optional<Long> messageServerIDOrNull)
|
||||||
throws StorageFailedException
|
throws StorageFailedException
|
||||||
{
|
{
|
||||||
Recipient originalRecipient = getMessageDestination(content, message);
|
Recipient originalRecipient = getRecipientForMessage(content, message);
|
||||||
Recipient primaryDeviceRecipient = getMessagePrimaryDestination(content, message);
|
Recipient masterRecipient = getMasterRecipientForMessage(content, message);
|
||||||
|
|
||||||
notifyTypingStoppedFromIncomingMessage(primaryDeviceRecipient, content.getSender(), content.getSenderDevice());
|
notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice());
|
||||||
|
|
||||||
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
|
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
|
||||||
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
||||||
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
|
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
|
||||||
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
||||||
|
|
||||||
Address sender = primaryDeviceRecipient.getAddress();
|
Address sender = masterRecipient.getAddress();
|
||||||
|
|
||||||
// If message is from group then we need to map it to get the sender of the message
|
// If message is from group then we need to map it to get the sender of the message
|
||||||
if (message.isGroupMessage()) {
|
if (message.isGroupMessage()) {
|
||||||
sender = getPrimaryDeviceRecipient(content.getSender()).getAddress();
|
sender = getMasterRecipient(content.getSender()).getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore messages from ourselves
|
// Ignore messages from ourselves
|
||||||
if (sender.serialize().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { return; }
|
if (sender.serialize().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { return; }
|
||||||
|
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender, message.getTimestamp(), -1,
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender, message.getTimestamp(), -1,
|
||||||
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
|
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
|
||||||
quote, sharedContacts, linkPreviews, sticker);
|
quote, sharedContacts, linkPreviews, sticker);
|
||||||
|
|
||||||
@ -905,12 +912,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Run db updates in the background, we should look into fixing this in the future
|
// Loki - Run database updates in the background, we should look into fixing this in the future
|
||||||
AsyncTask.execute(() -> {
|
AsyncTask.execute(() -> {
|
||||||
// Loki - Store message server ID
|
// Loki - Store message server ID
|
||||||
updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
|
updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
|
||||||
|
|
||||||
// Loki - Update mapping of message to original thread id
|
// Loki - Update mapping of message to original thread ID
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||||
@ -1028,10 +1035,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
@NonNull Optional<Long> messageServerIDOrNull)
|
@NonNull Optional<Long> messageServerIDOrNull)
|
||||||
throws StorageFailedException
|
throws StorageFailedException
|
||||||
{
|
{
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||||
String body = message.getBody().isPresent() ? message.getBody().get() : "";
|
String body = message.getBody().isPresent() ? message.getBody().get() : "";
|
||||||
Recipient originalRecipient = getMessageDestination(content, message);
|
Recipient originalRecipient = getRecipientForMessage(content, message);
|
||||||
Recipient primaryDeviceRecipient = getMessagePrimaryDestination(content, message);
|
Recipient masterRecipient = getMasterRecipientForMessage(content, message);
|
||||||
|
|
||||||
if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) {
|
if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) {
|
||||||
handleExpirationUpdate(content, message, Optional.absent());
|
handleExpirationUpdate(content, message, Optional.absent());
|
||||||
@ -1042,26 +1049,26 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
|
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
|
||||||
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
|
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
|
||||||
} else {
|
} else {
|
||||||
notifyTypingStoppedFromIncomingMessage(primaryDeviceRecipient, content.getSender(), content.getSenderDevice());
|
notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice());
|
||||||
|
|
||||||
Address sender = primaryDeviceRecipient.getAddress();
|
Address sender = masterRecipient.getAddress();
|
||||||
|
|
||||||
// If message is from group then we need to map it to get the sender of the message
|
// If message is from group then we need to map it to get the sender of the message
|
||||||
if (message.isGroupMessage()) {
|
if (message.isGroupMessage()) {
|
||||||
sender = getPrimaryDeviceRecipient(content.getSender()).getAddress();
|
sender = getMasterRecipient(content.getSender()).getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore messages from ourselves
|
// Ignore messages from ourselves
|
||||||
if (sender.serialize().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { return; }
|
if (sender.serialize().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { return; }
|
||||||
|
|
||||||
IncomingTextMessage _textMessage = new IncomingTextMessage(sender,
|
IncomingTextMessage tm = new IncomingTextMessage(sender,
|
||||||
content.getSenderDevice(),
|
content.getSenderDevice(),
|
||||||
message.getTimestamp(), body,
|
message.getTimestamp(), body,
|
||||||
message.getGroupInfo(),
|
message.getGroupInfo(),
|
||||||
message.getExpiresInSeconds() * 1000L,
|
message.getExpiresInSeconds() * 1000L,
|
||||||
content.isNeedsReceipt());
|
content.isNeedsReceipt());
|
||||||
|
|
||||||
IncomingEncryptedMessage textMessage = new IncomingEncryptedMessage(_textMessage, body);
|
IncomingEncryptedMessage textMessage = new IncomingEncryptedMessage(tm, body);
|
||||||
|
|
||||||
// Ignore the message if the body is empty
|
// Ignore the message if the body is empty
|
||||||
if (textMessage.getMessageBody().length() == 0) { return; }
|
if (textMessage.getMessageBody().length() == 0) { return; }
|
||||||
@ -1079,7 +1086,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
MessageNotifier.updateNotification(context, threadId);
|
MessageNotifier.updateNotification(context, threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loki - Run db updates in background, we should look into fixing this in the future
|
// Loki - Run database updates in background, we should look into fixing this in the future
|
||||||
AsyncTask.execute(() -> {
|
AsyncTask.execute(() -> {
|
||||||
if (insertResult.isPresent()) {
|
if (insertResult.isPresent()) {
|
||||||
InsertResult result = insertResult.get();
|
InsertResult result = insertResult.get();
|
||||||
@ -1090,7 +1097,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
// Loki - Store message server ID
|
// Loki - Store message server ID
|
||||||
updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
|
updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
|
||||||
|
|
||||||
// Loki - Update mapping of message to original thread id
|
// Loki - Update mapping of message to original thread ID
|
||||||
if (result.getMessageId() > -1) {
|
if (result.getMessageId() > -1) {
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
|
||||||
@ -1102,95 +1109,86 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidPairingMessage(@NonNull PairingAuthorisation authorisation) {
|
private boolean isValidDeviceLinkMessage(@NonNull DeviceLink authorisation) {
|
||||||
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
|
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
boolean isRequest = (authorisation.getType() == PairingAuthorisation.Type.REQUEST);
|
boolean isRequest = (authorisation.getType() == DeviceLink.Type.REQUEST);
|
||||||
if (authorisation.getRequestSignature() == null) {
|
if (authorisation.getRequestSignature() == null) {
|
||||||
Log.d("Loki", "Ignoring pairing request message without a request signature.");
|
Log.d("Loki", "Ignoring pairing request message without a request signature.");
|
||||||
return false;
|
return false;
|
||||||
} else if (isRequest && isSecondaryDevice) {
|
} else if (isRequest && isSecondaryDevice) {
|
||||||
Log.d("Loki", "Ignoring unexpected pairing request message (the device is already paired as a secondary device).");
|
Log.d("Loki", "Ignoring unexpected pairing request message (the device is already paired as a secondary device).");
|
||||||
return false;
|
return false;
|
||||||
} else if (isRequest && !authorisation.getPrimaryDevicePublicKey().equals(userHexEncodedPublicKey)) {
|
} else if (isRequest && !authorisation.getMasterHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
|
||||||
Log.d("Loki", "Ignoring pairing request message addressed to another user.");
|
Log.d("Loki", "Ignoring pairing request message addressed to another user.");
|
||||||
return false;
|
return false;
|
||||||
} else if (isRequest && authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) {
|
} else if (isRequest && authorisation.getSlaveHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
|
||||||
Log.d("Loki", "Ignoring pairing request message from self.");
|
Log.d("Loki", "Ignoring pairing request message from self.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return authorisation.verify();
|
return authorisation.verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleProfileAvatar(SignalServiceContent content, String url) {
|
private void handleDeviceLinkMessage(@NonNull DeviceLink deviceLink, @NonNull SignalServiceContent content) {
|
||||||
Recipient primaryDevice = getPrimaryDeviceRecipient(content.getSender());
|
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePairingMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
|
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
if (authorisation.getType() == PairingAuthorisation.Type.REQUEST) {
|
if (deviceLink.getType() == DeviceLink.Type.REQUEST) {
|
||||||
handlePairingRequestMessage(authorisation, content);
|
handleDeviceLinkRequestMessage(deviceLink, content);
|
||||||
} else if (authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) {
|
} else if (deviceLink.getSlaveHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
|
||||||
handlePairingAuthorisationMessage(authorisation, content);
|
handleDeviceLinkAuthorizedMessage(deviceLink, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePairingRequestMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
|
private void handleDeviceLinkRequestMessage(@NonNull DeviceLink deviceLink, @NonNull SignalServiceContent content) {
|
||||||
boolean isValid = isValidPairingMessage(authorisation);
|
boolean isValid = isValidDeviceLinkMessage(deviceLink);
|
||||||
DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
|
DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
|
||||||
if (isValid && linkingSession.isListeningForLinkingRequests()) {
|
if (!isValid || !linkingSession.isListeningForLinkingRequests()) { return; }
|
||||||
// Loki - If we successfully received a request then we should store the PreKeyBundle
|
storePreKeyBundleIfNeeded(content);
|
||||||
storePreKeyBundleIfNeeded(content);
|
linkingSession.processLinkingRequest(deviceLink);
|
||||||
linkingSession.processLinkingRequest(authorisation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePairingAuthorisationMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
|
private void handleDeviceLinkAuthorizedMessage(@NonNull DeviceLink deviceLink, @NonNull SignalServiceContent content) {
|
||||||
// Prepare
|
// Check preconditions
|
||||||
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
|
boolean hasExistingDeviceLink = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
|
||||||
if (isSecondaryDevice) {
|
if (hasExistingDeviceLink) {
|
||||||
Log.d("Loki", "Ignoring unexpected pairing authorisation message (the device is already paired as a secondary device).");
|
Log.d("Loki", "Ignoring unexpected device link message (the device is already linked as a slave device).");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean isValid = isValidPairingMessage(authorisation);
|
boolean isValid = isValidDeviceLinkMessage(deviceLink);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
Log.d("Loki", "Ignoring invalid pairing authorisation message.");
|
Log.d("Loki", "Ignoring invalid device link message.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!DeviceLinkingSession.Companion.getShared().isListeningForLinkingRequests()) {
|
if (!DeviceLinkingSession.Companion.getShared().isListeningForLinkingRequests()) {
|
||||||
Log.d("Loki", "Ignoring pairing authorisation message.");
|
Log.d("Loki", "Ignoring device link message.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (authorisation.getType() != PairingAuthorisation.Type.GRANT) { return; }
|
if (deviceLink.getType() != DeviceLink.Type.AUTHORIZATION) { return; }
|
||||||
Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getPrimaryDevicePublicKey() + ".");
|
Log.d("Loki", "Received device link authorized message from: " + deviceLink.getMasterHexEncodedPublicKey() + ".");
|
||||||
// Save PreKeyBundle if for whatever reason we got one
|
// Save pre key bundle if we somehow got one
|
||||||
storePreKeyBundleIfNeeded(content);
|
storePreKeyBundleIfNeeded(content);
|
||||||
// Process
|
// Process
|
||||||
DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation);
|
DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(deviceLink);
|
||||||
// Store the primary device's public key
|
// Store the master device's ID
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).removePairingAuthorisations(userHexEncodedPublicKey);
|
DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userHexEncodedPublicKey);
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(authorisation);
|
DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink);
|
||||||
TextSecurePreferences.setMasterHexEncodedPublicKey(context, authorisation.getPrimaryDevicePublicKey());
|
TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.getMasterHexEncodedPublicKey());
|
||||||
TextSecurePreferences.setMultiDevice(context, true);
|
TextSecurePreferences.setMultiDevice(context, true);
|
||||||
// Send a background message to the primary device
|
// Send a background message to the master device
|
||||||
MessageSender.sendBackgroundMessage(context, authorisation.getPrimaryDevicePublicKey());
|
MessageSender.sendBackgroundMessage(context, deviceLink.getMasterHexEncodedPublicKey());
|
||||||
// Propagate the updates to the file server
|
// Update display name if needed
|
||||||
LokiStorageAPI storageAPI = LokiStorageAPI.Companion.getShared();
|
|
||||||
storageAPI.updateUserDeviceMappings();
|
|
||||||
// Update display names
|
|
||||||
if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
|
if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
|
||||||
TextSecurePreferences.setProfileName(context, content.senderDisplayName.get());
|
TextSecurePreferences.setProfileName(context, content.senderDisplayName.get());
|
||||||
}
|
}
|
||||||
// Profile avatar updates
|
// Update profile picture if needed
|
||||||
if (content.getDataMessage().isPresent()) {
|
if (content.getDataMessage().isPresent()) {
|
||||||
handleProfileKey(content, content.getDataMessage().get());
|
handleProfileKey(content, content.getDataMessage().get());
|
||||||
}
|
}
|
||||||
// Contact sync
|
// Handle contact sync if needed
|
||||||
if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) {
|
if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) {
|
||||||
handleSynchronizeContactMessage(content.getSyncMessage().get().getContacts().get());
|
handleContactSyncMessage(content.getSyncMessage().get().getContacts().get());
|
||||||
}
|
}
|
||||||
|
// The device link is propagated to the file server in LandingActivity.onDeviceLinkAuthorized because we can handle the error there
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDisplayName(String hexEncodedPublicKey, String profileName) {
|
private void setDisplayName(String hexEncodedPublicKey, String profileName) {
|
||||||
@ -1199,95 +1197,69 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateGroupChatMessageServerID(Optional<Long> messageServerIDOrNull, Optional<InsertResult> insertResult) {
|
private void updateGroupChatMessageServerID(Optional<Long> messageServerIDOrNull, Optional<InsertResult> insertResult) {
|
||||||
if (insertResult.isPresent() && messageServerIDOrNull.isPresent()) {
|
if (!insertResult.isPresent() || !messageServerIDOrNull.isPresent()) { return; }
|
||||||
long messageID = insertResult.get().getMessageId();
|
long messageID = insertResult.get().getMessageId();
|
||||||
long messageServerID = messageServerIDOrNull.get();
|
long messageServerID = messageServerIDOrNull.get();
|
||||||
DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, messageServerID);
|
DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, messageServerID);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceContent content) {
|
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceContent content) {
|
||||||
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
||||||
if (!sender.isGroupRecipient() && content.lokiServiceMessage.isPresent()) {
|
if (sender.isGroupRecipient() || !content.lokiServiceMessage.isPresent()) { return; }
|
||||||
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
|
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
|
||||||
if (lokiMessage.getPreKeyBundleMessage() != null) {
|
if (lokiMessage.getPreKeyBundleMessage() == null) { return; }
|
||||||
int registrationID = TextSecurePreferences.getLocalRegistrationId(context);
|
int registrationID = TextSecurePreferences.getLocalRegistrationId(context);
|
||||||
LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context);
|
LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context);
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
if (registrationID <= 0) { return; }
|
||||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
Log.d("Loki", "Received a pre key bundle from: " + content.getSender() + ".");
|
||||||
|
PreKeyBundle preKeyBundle = lokiMessage.getPreKeyBundleMessage().getPreKeyBundle(registrationID);
|
||||||
|
lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle);
|
||||||
|
|
||||||
// Loki - Store the latest pre key bundle
|
|
||||||
if (registrationID > 0) {
|
|
||||||
Log.d("Loki", "Received a pre key bundle from: " + content.getSender() + ".");
|
|
||||||
PreKeyBundle preKeyBundle = lokiMessage.getPreKeyBundleMessage().getPreKeyBundle(registrationID);
|
|
||||||
lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle);
|
|
||||||
|
|
||||||
// Loki - If we received a friend request, but we were already friends with this user, then reset the session
|
|
||||||
if (content.isFriendRequest()) {
|
|
||||||
long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
|
|
||||||
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
|
|
||||||
resetSession(content.getSender(), threadID);
|
|
||||||
// Let our other devices know that we have reset the session
|
|
||||||
MessageSender.syncContact(context, sender.getAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void acceptFriendRequestIfNeeded(@NonNull SignalServiceContent content) {
|
|
||||||
// If we get anything other than a friend request, we can assume that we have a session with the other user
|
|
||||||
if (content.isFriendRequest() || isGroupChatMessage(content)) { return; }
|
|
||||||
becomeFriendsWithContact(content.getSender(), true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSessionRequestIfNeeded(@NonNull SignalServiceContent content) {
|
private void handleSessionRequestIfNeeded(@NonNull SignalServiceContent content) {
|
||||||
if (content.isFriendRequest() && isSessionRequest(content)) {
|
if (!content.isFriendRequest() || !isSessionRequest(content)) { return; }
|
||||||
// Check if the session request from a member in one of our groups or our friend
|
// Check if the session request came from a member in one of our groups or one of our friends
|
||||||
LokiStorageAPI.shared.getPrimaryDevicePublicKey(content.getSender()).success(primaryDevicePublicKey -> {
|
LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(content.getSender()).success( masterHexEncodedPublicKey -> {
|
||||||
String sender = primaryDevicePublicKey != null ? primaryDevicePublicKey : content.getSender();
|
String sender = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : content.getSender();
|
||||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(sender), false));
|
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(sender), false));
|
||||||
LokiThreadFriendRequestStatus threadFriendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
|
LokiThreadFriendRequestStatus threadFriendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
|
||||||
boolean isOurFriend = threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS;
|
boolean isOurFriend = threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS;
|
||||||
boolean isInOneOfOurGroups = DatabaseFactory.getGroupDatabase(context).signalGroupsHaveMember(sender);
|
boolean isInOneOfOurGroups = DatabaseFactory.getGroupDatabase(context).signalGroupsHaveMember(sender);
|
||||||
boolean shouldAcceptSessionRequest = isOurFriend || isInOneOfOurGroups;
|
boolean shouldAcceptSessionRequest = isOurFriend || isInOneOfOurGroups;
|
||||||
if (shouldAcceptSessionRequest) {
|
if (shouldAcceptSessionRequest) {
|
||||||
// Send a background message to acknowledge session request
|
MessageSender.sendBackgroundMessage(context, content.getSender()); // Send a background message to acknowledge
|
||||||
MessageSender.sendBackgroundMessage(context, content.getSender());
|
}
|
||||||
}
|
return Unit.INSTANCE;
|
||||||
return Unit.INSTANCE;
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void becomeFriendsWithContact(String pubKey, boolean syncContact, boolean force) {
|
private void becomeFriendsWithContactIfNeeded(String hexEncodedPublicKey, boolean requiresContactSync, boolean canSkip) {
|
||||||
|
// Ignore friend requests to group recipients
|
||||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
||||||
Recipient contactID = Recipient.from(context, Address.fromSerialized(pubKey), false);
|
Recipient contactID = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
|
||||||
if (contactID.isGroupRecipient()) return;
|
if (contactID.isGroupRecipient()) return;
|
||||||
|
// Ignore friend requests to recipients we're already friends with
|
||||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(contactID);
|
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(contactID);
|
||||||
LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
|
LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
|
||||||
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) { return; }
|
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) { return; }
|
||||||
|
// We shouldn't be able to skip from NONE to FRIENDS under normal circumstances.
|
||||||
// We shouldn't be able to skip from None -> Friends in normal circumstances.
|
// Multi-device is the one exception to this rule because we want to automatically become friends with slave devices.
|
||||||
// Multi-device is the exception to this rule because we want to automatically be friends with a secondary device
|
if (!canSkip && threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE) { return; }
|
||||||
if (!force && threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE) { return; }
|
// If the thread's friend request status is not `FRIENDS` or `NONE`, but we're receiving a message,
|
||||||
|
|
||||||
// If the thread's friend request status is not `FRIENDS`, but we're receiving a message,
|
|
||||||
// it must be a friend request accepted message. Declining a friend request doesn't send a message.
|
// it must be a friend request accepted message. Declining a friend request doesn't send a message.
|
||||||
lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
||||||
// Send out a contact sync message
|
// Send out a contact sync message if needed
|
||||||
if (syncContact) {
|
if (requiresContactSync) {
|
||||||
MessageSender.syncContact(context, contactID.getAddress());
|
MessageSender.syncContact(context, contactID.getAddress());
|
||||||
}
|
}
|
||||||
// Allow profile sharing with contact
|
// Enable profile sharing with the recipient
|
||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(contactID, true);
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(contactID, true);
|
||||||
// Update the last message if needed
|
// Update the last message if needed
|
||||||
LokiStorageAPI.shared.getPrimaryDevicePublicKey(pubKey).success(primaryDevice -> {
|
LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey).success( masterHexEncodedPublicKey -> {
|
||||||
Util.runOnMain(() -> {
|
Util.runOnMain(() -> {
|
||||||
long primaryDeviceThreadID = primaryDevice == null ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(primaryDevice), false));
|
long masterThreadID = (masterHexEncodedPublicKey == null) ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(masterHexEncodedPublicKey), false));
|
||||||
FriendRequestHandler.updateLastFriendRequestMessage(context, primaryDeviceThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
|
FriendRequestHandler.updateLastFriendRequestMessage(context, masterThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
|
||||||
});
|
});
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
@ -1295,25 +1267,24 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
|
|
||||||
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
|
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
|
||||||
if (!content.isFriendRequest() || message.isGroupMessage() || message.isSessionRequest()) { return; }
|
if (!content.isFriendRequest() || message.isGroupMessage() || message.isSessionRequest()) { return; }
|
||||||
// This handles the case where another user sends us a regular message without authorisation
|
|
||||||
Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
|
Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
|
||||||
boolean shouldBecomeFriends = PromiseUtil.get(promise, false);
|
boolean shouldBecomeFriends = PromiseUtil.get(promise, false);
|
||||||
if (shouldBecomeFriends) {
|
if (shouldBecomeFriends) {
|
||||||
// Become friends AND update the message they sent
|
// Become friends AND update the message they sent
|
||||||
becomeFriendsWithContact(content.getSender(), true, true);
|
becomeFriendsWithContactIfNeeded(content.getSender(), true, true);
|
||||||
// Send them an accept message back
|
// Send them an accept message back
|
||||||
MessageSender.sendBackgroundMessage(context, content.getSender());
|
MessageSender.sendBackgroundMessage(context, content.getSender());
|
||||||
} else {
|
} else {
|
||||||
// Do regular friend request logic checks
|
// Do regular friend request logic checks
|
||||||
Recipient originalRecipient = getMessageDestination(content, message);
|
Recipient originalRecipient = getRecipientForMessage(content, message);
|
||||||
Recipient primaryDeviceRecipient = getMessagePrimaryDestination(content, message);
|
Recipient masterRecipient = getMasterRecipientForMessage(content, message);
|
||||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
||||||
|
|
||||||
// Loki - Friend requests only work in direct chats
|
// Loki - Friend requests only work in direct chats
|
||||||
if (!originalRecipient.getAddress().isPhone()) { return; }
|
if (!originalRecipient.getAddress().isPhone()) { return; }
|
||||||
|
|
||||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(originalRecipient);
|
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(originalRecipient);
|
||||||
long primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(primaryDeviceRecipient);
|
long primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(masterRecipient);
|
||||||
LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
|
LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
|
||||||
|
|
||||||
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
|
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
|
||||||
@ -1482,7 +1453,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void triggerSessionRestorePrompt(@NonNull String sender) {
|
private void triggerSessionRestorePrompt(@NonNull String sender) {
|
||||||
Recipient primaryRecipient = getPrimaryDeviceRecipient(sender);
|
Recipient primaryRecipient = getMasterRecipient(sender);
|
||||||
if (!primaryRecipient.isGroupRecipient()) {
|
if (!primaryRecipient.isGroupRecipient()) {
|
||||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(primaryRecipient);
|
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(primaryRecipient);
|
||||||
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, sender);
|
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, sender);
|
||||||
@ -1537,12 +1508,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
|
if (recipient.getProfileKey() == null || !MessageDigest.isEqual(recipient.getProfileKey(), message.getProfileKey().get())) {
|
||||||
database.setProfileKey(recipient, message.getProfileKey().get());
|
database.setProfileKey(recipient, message.getProfileKey().get());
|
||||||
database.setUnidentifiedAccessMode(recipient, RecipientDatabase.UnidentifiedAccessMode.UNKNOWN);
|
database.setUnidentifiedAccessMode(recipient, RecipientDatabase.UnidentifiedAccessMode.UNKNOWN);
|
||||||
String url = content.senderProfileAvatarUrl.or("");
|
String url = content.senderProfilePictureURL.or("");
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(recipient, url));
|
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(recipient, url));
|
||||||
|
|
||||||
// Loki - If the recipient is our master device then we need to go and update our avatar mappings on the public chats
|
// Loki - If the recipient is our master device then we need to go and update our avatar mappings on the public chats
|
||||||
if (recipient.isOurMasterDevice()) {
|
if (recipient.isOurMasterDevice()) {
|
||||||
ApplicationContext.getInstance(context).updatePublicChatProfileAvatarIfNeeded();
|
ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1559,11 +1530,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
|
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceReceiptMessage message)
|
@NonNull SignalServiceReceiptMessage message)
|
||||||
{
|
{
|
||||||
// Redirect message to primary device conversation
|
// Redirect message to master device conversation
|
||||||
Address sender = Address.fromSerialized(content.getSender());
|
Address sender = Address.fromSerialized(content.getSender());
|
||||||
if (sender.isPhone()) {
|
if (sender.isPhone()) {
|
||||||
Recipient primaryDevice = getPrimaryDeviceRecipient(content.getSender());
|
Recipient masterDevice = getMasterRecipient(content.getSender());
|
||||||
sender = primaryDevice.getAddress();
|
sender = masterDevice.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long timestamp : message.getTimestamps()) {
|
for (long timestamp : message.getTimestamps()) {
|
||||||
@ -1579,11 +1550,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
{
|
{
|
||||||
if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||||
|
|
||||||
// Redirect message to primary device conversation
|
// Redirect message to master device conversation
|
||||||
Address sender = Address.fromSerialized(content.getSender());
|
Address sender = Address.fromSerialized(content.getSender());
|
||||||
if (sender.isPhone()) {
|
if (sender.isPhone()) {
|
||||||
Recipient primaryDevice = getPrimaryDeviceRecipient(content.getSender());
|
Recipient masterDevice = getMasterRecipient(content.getSender());
|
||||||
sender = primaryDevice.getAddress();
|
sender = masterDevice.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long timestamp : message.getTimestamps()) {
|
for (long timestamp : message.getTimestamps()) {
|
||||||
@ -1614,7 +1585,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
|
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
|
||||||
} else {
|
} else {
|
||||||
// See if we need to redirect the message
|
// See if we need to redirect the message
|
||||||
author = getPrimaryDeviceRecipient(content.getSender());
|
author = getMasterRecipient(content.getSender());
|
||||||
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(author);
|
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(author);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1748,9 +1719,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) {
|
private Optional<InsertResult> insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) {
|
||||||
Recipient primaryDevice = getPrimaryDeviceRecipient(sender);
|
Recipient masterDevice = getMasterRecipient(sender);
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||||
IncomingTextMessage textMessage = new IncomingTextMessage(primaryDevice.getAddress(),
|
IncomingTextMessage textMessage = new IncomingTextMessage(masterDevice.getAddress(),
|
||||||
senderDevice, timestamp, "",
|
senderDevice, timestamp, "",
|
||||||
Optional.absent(), 0, false);
|
Optional.absent(), 0, false);
|
||||||
|
|
||||||
@ -1770,11 +1741,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (message.getMessage().isGroupMessage()) {
|
if (message.getMessage().isGroupMessage()) {
|
||||||
return getSyncMessageDestination(message);
|
return getSyncMessageDestination(message);
|
||||||
} else {
|
} else {
|
||||||
return getPrimaryDeviceRecipient(message.getDestination().get());
|
return getMasterRecipient(message.getDestination().get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
private Recipient getRecipientForMessage(SignalServiceContent content, SignalServiceDataMessage message) {
|
||||||
if (message.isGroupMessage()) {
|
if (message.isGroupMessage()) {
|
||||||
return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get())), false);
|
return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get())), false);
|
||||||
} else {
|
} else {
|
||||||
@ -1782,34 +1753,34 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipient getMessagePrimaryDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
private Recipient getMasterRecipientForMessage(SignalServiceContent content, SignalServiceDataMessage message) {
|
||||||
if (message.isGroupMessage()) {
|
if (message.isGroupMessage()) {
|
||||||
return getMessageDestination(content, message);
|
return getRecipientForMessage(content, message);
|
||||||
} else {
|
} else {
|
||||||
return getPrimaryDeviceRecipient(content.getSender());
|
return getMasterRecipient(content.getSender());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the primary device recipient of the passed in device.
|
* Get the master device recipient of the provided device.
|
||||||
*
|
*
|
||||||
* If the device doesn't have a primary device then it will return the same device.
|
* If the device doesn't have a master device this will return the same device.
|
||||||
* If the device is our primary device then it will return our current device.
|
* If the device is our master device then it will return our current device.
|
||||||
* Otherwise it will return the primary device.
|
* Otherwise it will return the master device.
|
||||||
*/
|
*/
|
||||||
private Recipient getPrimaryDeviceRecipient(String pubKey) {
|
private Recipient getMasterRecipient(String hexEncodedPublicKey) {
|
||||||
try {
|
try {
|
||||||
String primaryDevice = PromiseUtil.timeout(LokiStorageAPI.shared.getPrimaryDevicePublicKey(pubKey), 5000).get();
|
String masterHexEncodedPublicKey = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
|
||||||
String publicKey = (primaryDevice != null) ? primaryDevice : pubKey;
|
String targetHexEncodedPublicKey = (masterHexEncodedPublicKey != null) ? masterHexEncodedPublicKey : hexEncodedPublicKey;
|
||||||
// If the public key matches our primary device then we need to forward the message to ourselves (Note to self)
|
// If the public key matches our master device then we need to forward the message to ourselves (note to self)
|
||||||
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
String ourMasterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(publicKey)) {
|
if (ourMasterHexEncodedPublicKey != null && ourMasterHexEncodedPublicKey.equals(targetHexEncodedPublicKey)) {
|
||||||
publicKey = TextSecurePreferences.getLocalNumber(context);
|
targetHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
}
|
}
|
||||||
return Recipient.from(context, Address.fromSerialized(publicKey), false);
|
return Recipient.from(context, Address.fromSerialized(targetHexEncodedPublicKey), false);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.d("Loki", "Failed to get primary device public key for " + pubKey + ". " + e.getMessage());
|
Log.d("Loki", "Failed to get master device for: " + hexEncodedPublicKey + ". " + e.getMessage());
|
||||||
return Recipient.from(context, Address.fromSerialized(pubKey), false);
|
return Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1831,11 +1802,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
|
|
||||||
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
||||||
|
|
||||||
if (content.getPairingAuthorisation().isPresent()) {
|
if (content.getDeviceLink().isPresent()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (content.getDataMessage().isPresent()) {
|
} else if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
Recipient conversation = getMessageDestination(content, message);
|
Recipient conversation = getRecipientForMessage(content, message);
|
||||||
|
|
||||||
if (conversation.isGroupRecipient() && conversation.isBlocked()) {
|
if (conversation.isGroupRecipient() && conversation.isBlocked()) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -50,8 +50,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
|||||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
|
import org.whispersystems.signalservice.loki.api.LokiPublicChat;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
|
||||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -368,7 +368,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
|||||||
if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; }
|
if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<String> secondaryDevices = PromiseUtil.timeout(LokiStorageAPI.shared.getSecondaryDevicePublicKeys(member.serialize()), 5000).get();
|
Set<String> secondaryDevices = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(member.serialize()), 5000).get();
|
||||||
memberSet.addAll(Stream.of(secondaryDevices).map(string -> {
|
memberSet.addAll(Stream.of(secondaryDevices).map(string -> {
|
||||||
// Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :(
|
// Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :(
|
||||||
return Address.fromSerialized(string);
|
return Address.fromSerialized(string);
|
||||||
|
@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
@ -43,13 +42,12 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
|
|||||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
|
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
|
||||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -291,11 +289,11 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
|||||||
} else {
|
} else {
|
||||||
LokiSyncMessage syncMessage = null;
|
LokiSyncMessage syncMessage = null;
|
||||||
if (shouldSendSyncMessage) {
|
if (shouldSendSyncMessage) {
|
||||||
// Set the sync message destination the primary device, this way it will show that we sent a message to the primary device and not a secondary device
|
// Set the sync message destination to the master device, this way it will show that we sent a message to the master device and not the slave device
|
||||||
String primaryDevice = PromiseUtil.get(LokiStorageAPI.shared.getPrimaryDevicePublicKey(address.getNumber()), null);
|
String masterDevice = PromiseUtil.get(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(address.getNumber()), null);
|
||||||
SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice);
|
SignalServiceAddress masterAddress = masterDevice == null ? address : new SignalServiceAddress(masterDevice);
|
||||||
// We also need to use the original message id and not -1
|
// We also need to use the original message ID and not -1
|
||||||
syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId);
|
syncMessage = new LokiSyncMessage(masterAddress, templateMessageId);
|
||||||
}
|
}
|
||||||
return messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage, Optional.fromNullable(syncMessage)).getSuccess().isUnidentified();
|
return messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage, Optional.fromNullable(syncMessage)).getSuccess().isUnidentified();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
|||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
@ -30,7 +29,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
|||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
|
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
|
||||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||||
|
|
||||||
@ -201,7 +200,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// rotateSenderCertificateIfNecessary();
|
// rotateSenderCertificateIfNecessary();
|
||||||
Recipient recipient = Recipient.from(context, destination, false);
|
Recipient recipient = Recipient.from(context, destination, false);
|
||||||
SignalServiceAddress address = getPushAddress(recipient.getAddress());
|
SignalServiceAddress address = getPushAddress(recipient.getAddress());
|
||||||
Optional<byte[]> profileKey = getProfileKey(recipient);
|
Optional<byte[]> profileKey = getProfileKey(recipient);
|
||||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
|
||||||
@ -236,11 +235,11 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
} else {
|
} else {
|
||||||
LokiSyncMessage syncMessage = null;
|
LokiSyncMessage syncMessage = null;
|
||||||
if (shouldSendSyncMessage) {
|
if (shouldSendSyncMessage) {
|
||||||
// Set the sync message destination to the primary device, this way it will show that we sent a message to the primary device and not a secondary device
|
// Set the sync message destination to the master device, this way it will show that we sent a message to the master device and not the slave device
|
||||||
String primaryDevice = PromiseUtil.get(LokiStorageAPI.shared.getPrimaryDevicePublicKey(address.getNumber()), null);
|
String masterDevice = PromiseUtil.get(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(address.getNumber()), null);
|
||||||
SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice);
|
SignalServiceAddress masterAddress = masterDevice == null ? address : new SignalServiceAddress(masterDevice);
|
||||||
// We also need to use the original message id and not -1
|
// We also need to use the original message ID and not -1
|
||||||
syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId);
|
syncMessage = new LokiSyncMessage(masterAddress, templateMessageId);
|
||||||
}
|
}
|
||||||
return messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, Optional.fromNullable(syncMessage)).getSuccess().isUnidentified();
|
return messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, Optional.fromNullable(syncMessage)).getSuccess().isUnidentified();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
|
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol
|
||||||
|
import org.whispersystems.libsignal.loki.LokiSessionResetStatus
|
||||||
|
import org.whispersystems.libsignal.protocol.PreKeySignalMessage
|
||||||
|
|
||||||
|
class LokiSessionResetImplementation(private val context: Context) : LokiSessionResetProtocol {
|
||||||
|
|
||||||
|
override fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus {
|
||||||
|
return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(hexEncodedPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) {
|
||||||
|
return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(hexEncodedPublicKey, sessionResetStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewSessionAdopted(hexEncodedPublicKey: String, oldSessionResetStatus: LokiSessionResetStatus) {
|
||||||
|
if (oldSessionResetStatus == LokiSessionResetStatus.IN_PROGRESS) {
|
||||||
|
// Send a message back to the contact to finalise session reset
|
||||||
|
MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Show session reset succeed message
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validatePreKeySignalMessage(sender: String, message: PreKeySignalMessage) {
|
||||||
|
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(sender)
|
||||||
|
check(preKeyRecord != null) { "Received a background message from a user without an associated pre key record." }
|
||||||
|
check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
|
||||||
|
}
|
||||||
|
}
|
@ -10,11 +10,11 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
|||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.*
|
import org.thoughtcrime.securesms.loki.redesign.utilities.*
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
import org.whispersystems.libsignal.loki.LokiSessionResetStatus
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChat
|
import org.whispersystems.signalservice.loki.api.LokiPublicChat
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadDatabaseProtocol
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadDatabaseProtocol
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadSessionResetStatus
|
|
||||||
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
|
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
|
||||||
|
|
||||||
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol {
|
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol {
|
||||||
@ -23,11 +23,11 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
|||||||
companion object {
|
companion object {
|
||||||
private val friendRequestTableName = "loki_thread_friend_request_database"
|
private val friendRequestTableName = "loki_thread_friend_request_database"
|
||||||
private val sessionResetTableName = "loki_thread_session_reset_database"
|
private val sessionResetTableName = "loki_thread_session_reset_database"
|
||||||
public val publicChatTableName = "loki_public_chat_database"
|
val publicChatTableName = "loki_public_chat_database"
|
||||||
public val threadID = "thread_id"
|
val threadID = "thread_id"
|
||||||
private val friendRequestStatus = "friend_request_status"
|
private val friendRequestStatus = "friend_request_status"
|
||||||
private val sessionResetStatus = "session_reset_status"
|
private val sessionResetStatus = "session_reset_status"
|
||||||
public val publicChat = "public_chat"
|
val publicChat = "public_chat"
|
||||||
@JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTableName ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);"
|
@JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTableName ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);"
|
||||||
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTableName ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
@JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTableName ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);"
|
||||||
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTableName ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
@JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTableName ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);"
|
||||||
@ -79,19 +79,21 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
|
|||||||
|| friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED
|
|| friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSessionResetStatus(threadID: Long): LokiThreadSessionResetStatus {
|
fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus {
|
||||||
|
val threadID = getThreadID(hexEncodedPublicKey)
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
val result = database.get(sessionResetTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
val result = database.get(sessionResetTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
|
||||||
cursor.getInt(sessionResetStatus)
|
cursor.getInt(sessionResetStatus)
|
||||||
}
|
}
|
||||||
return if (result != null) {
|
return if (result != null) {
|
||||||
LokiThreadSessionResetStatus.values().first { it.rawValue == result }
|
LokiSessionResetStatus.values().first { it.rawValue == result }
|
||||||
} else {
|
} else {
|
||||||
LokiThreadSessionResetStatus.NONE
|
LokiSessionResetStatus.NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setSessionResetStatus(threadID: Long, sessionResetStatus: LokiThreadSessionResetStatus) {
|
fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) {
|
||||||
|
val threadID = getThreadID(hexEncodedPublicKey)
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val contentValues = ContentValues(2)
|
val contentValues = ContentValues(2)
|
||||||
contentValues.put(Companion.threadID, threadID)
|
contentValues.put(Companion.threadID, threadID)
|
||||||
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.loki
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.all
|
|
||||||
import nl.komponents.kovenant.functional.bind
|
import nl.komponents.kovenant.functional.bind
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import nl.komponents.kovenant.toFailVoid
|
import nl.komponents.kovenant.toFailVoid
|
||||||
@ -16,40 +15,36 @@ import org.thoughtcrime.securesms.database.Address
|
|||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
import org.thoughtcrime.securesms.logging.Log
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
||||||
import org.whispersystems.signalservice.loki.utilities.recover
|
import org.whispersystems.signalservice.loki.utilities.recover
|
||||||
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
|
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
|
||||||
import java.util.*
|
|
||||||
import kotlin.concurrent.schedule
|
|
||||||
|
|
||||||
fun checkForRevocation(context: Context) {
|
fun checkIsRevokedSlaveDevice(context: Context) {
|
||||||
val primaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
|
val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
|
||||||
val ourDevice = TextSecurePreferences.getLocalNumber(context)
|
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
|
LokiFileServerAPI.shared.getDeviceLinks(masterHexEncodedPublicKey, true).bind { deviceLinks ->
|
||||||
LokiStorageAPI.shared.fetchDeviceMappings(primaryDevice).bind { mappings ->
|
val deviceLink = deviceLinks.find { it.masterHexEncodedPublicKey == masterHexEncodedPublicKey && it.slaveHexEncodedPublicKey == hexEncodedPublicKey }
|
||||||
val ourMapping = mappings.find { it.secondaryDevicePublicKey == ourDevice }
|
if (deviceLink != null) throw Error("Device hasn't been revoked.")
|
||||||
if (ourMapping != null) throw Error("Device has not been revoked")
|
DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(hexEncodedPublicKey)
|
||||||
// remove pairing authorisations for our device
|
LokiFileServerAPI.shared.setDeviceLinks(setOf())
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).removePairingAuthorisations(ourDevice)
|
|
||||||
LokiStorageAPI.shared.updateUserDeviceMappings()
|
|
||||||
}.successUi {
|
}.successUi {
|
||||||
TextSecurePreferences.setNeedsRevocationCheck(context, false)
|
TextSecurePreferences.setNeedsIsRevokedSlaveDeviceCheck(context, false)
|
||||||
ApplicationContext.getInstance(context).clearData()
|
ApplicationContext.getInstance(context).clearData()
|
||||||
}.fail { error ->
|
}.fail { error ->
|
||||||
TextSecurePreferences.setNeedsRevocationCheck(context, true)
|
TextSecurePreferences.setNeedsIsRevokedSlaveDeviceCheck(context, true)
|
||||||
Log.d("Loki", "Revocation check failed: ${error.message ?: error}")
|
Log.d("Loki", "Revocation check failed due to error: ${error.message ?: error}.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
|
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
|
||||||
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
|
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
|
||||||
return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys ->
|
return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys ->
|
||||||
val map = mutableMapOf<String, LokiThreadFriendRequestStatus>()
|
val map = mutableMapOf<String, LokiThreadFriendRequestStatus>()
|
||||||
for (devicePublicKey in keys) {
|
for (devicePublicKey in keys) {
|
||||||
val device = Recipient.from(context, Address.fromSerialized(devicePublicKey), false)
|
val device = Recipient.from(context, Address.fromSerialized(devicePublicKey), false)
|
||||||
@ -63,7 +58,7 @@ fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: Str
|
|||||||
|
|
||||||
fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Promise<Map<String, Boolean>, Unit> {
|
fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Promise<Map<String, Boolean>, Unit> {
|
||||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys ->
|
return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys ->
|
||||||
val devices = keys.toMutableSet()
|
val devices = keys.toMutableSet()
|
||||||
if (hexEncodedPublicKey != userHexEncodedPublicKey) {
|
if (hexEncodedPublicKey != userHexEncodedPublicKey) {
|
||||||
devices.remove(userHexEncodedPublicKey)
|
devices.remove(userHexEncodedPublicKey)
|
||||||
@ -92,7 +87,7 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Conte
|
|||||||
return Promise.of(true)
|
return Promise.of(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return LokiStorageAPI.shared.getPrimaryDevicePublicKey(publicKey).bind { primaryDevicePublicKey ->
|
return LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(publicKey).bind { primaryDevicePublicKey ->
|
||||||
// If the public key doesn't have any other devices then go through regular friend request logic
|
// If the public key doesn't have any other devices then go through regular friend request logic
|
||||||
if (primaryDevicePublicKey == null) {
|
if (primaryDevicePublicKey == null) {
|
||||||
return@bind Promise.of(false)
|
return@bind Promise.of(false)
|
||||||
@ -108,66 +103,44 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise<Unit, Exception> {
|
fun sendDeviceLinkMessage(context: Context, hexEncodedPublicKey: String, deviceLink: DeviceLink): Promise<Unit, Exception> {
|
||||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||||
val address = SignalServiceAddress(contactHexEncodedPublicKey)
|
val address = SignalServiceAddress(hexEncodedPublicKey)
|
||||||
val message = SignalServiceDataMessage.newBuilder().withPairingAuthorisation(authorisation)
|
val message = SignalServiceDataMessage.newBuilder().withDeviceLink(deviceLink)
|
||||||
// A REQUEST should always act as a friend request. A GRANT should always be replying back as a normal message.
|
// A REQUEST should always act as a friend request. An AUTHORIZATION should always be a normal message.
|
||||||
if (authorisation.type == PairingAuthorisation.Type.REQUEST) {
|
if (deviceLink.type == DeviceLink.Type.REQUEST) {
|
||||||
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number)
|
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number)
|
||||||
message.asFriendRequest(true).withPreKeyBundle(preKeyBundle)
|
message.asFriendRequest(true).withPreKeyBundle(preKeyBundle)
|
||||||
} else {
|
} else {
|
||||||
// Send over our profile key so that our linked device can get our profile picture
|
// Send over our profile key so that our linked device can get our profile picture
|
||||||
message.withProfileKey(ProfileKeyUtil.getProfileKey(context))
|
message.withProfileKey(ProfileKeyUtil.getProfileKey(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
Log.d("Loki", "Sending authorisation message to: $contactHexEncodedPublicKey.")
|
Log.d("Loki", "Sending device link message to: $hexEncodedPublicKey.")
|
||||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(contactHexEncodedPublicKey), false))
|
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false))
|
||||||
val result = messageSender.sendMessage(0, address, udAccess, message.build())
|
val result = messageSender.sendMessage(0, address, udAccess, message.build())
|
||||||
if (result.success == null) {
|
if (result.success == null) {
|
||||||
val exception = when {
|
val exception = when {
|
||||||
result.isNetworkFailure -> "Failed to send authorisation message due to a network error."
|
result.isNetworkFailure -> "Failed to send device link message due to a network error."
|
||||||
else -> "Failed to send authorisation message."
|
else -> "Failed to send device link message."
|
||||||
}
|
}
|
||||||
throw Exception(exception)
|
throw Exception(exception)
|
||||||
}
|
}
|
||||||
Promise.ofSuccess(Unit)
|
Promise.ofSuccess(Unit)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d("Loki", "Failed to send authorisation message to: $contactHexEncodedPublicKey.")
|
Log.d("Loki", "Failed to send device link message to: $hexEncodedPublicKey.")
|
||||||
Promise.ofFail(e)
|
Promise.ofFail(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisation: PairingAuthorisation) {
|
fun signAndSendDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Promise<Unit, Exception> {
|
||||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||||
val signedPairingAuthorisation = pairingAuthorisation.sign(PairingAuthorisation.Type.GRANT, userPrivateKey)
|
val signedDeviceLink = deviceLink.sign(DeviceLink.Type.AUTHORIZATION, userPrivateKey)
|
||||||
if (signedPairingAuthorisation == null || signedPairingAuthorisation.type != PairingAuthorisation.Type.GRANT) {
|
if (signedDeviceLink == null || signedDeviceLink.type != DeviceLink.Type.AUTHORIZATION) {
|
||||||
Log.d("Loki", "Failed to sign pairing authorization.")
|
return Promise.ofFail(Exception("Failed to sign device link."))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedPairingAuthorisation)
|
return retryIfNeeded(8) {
|
||||||
TextSecurePreferences.setMultiDevice(context, true)
|
sendDeviceLinkMessage(context, deviceLink.slaveHexEncodedPublicKey, signedDeviceLink)
|
||||||
|
|
||||||
val address = Address.fromSerialized(pairingAuthorisation.secondaryDevicePublicKey);
|
|
||||||
|
|
||||||
val sendPromise = retryIfNeeded(8) {
|
|
||||||
sendPairingAuthorisationMessage(context, address.serialize(), signedPairingAuthorisation)
|
|
||||||
}.fail {
|
|
||||||
Log.d("Loki", "Failed to send pairing authorization message to ${address.serialize()}.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val updatePromise = LokiStorageAPI.shared.updateUserDeviceMappings().fail {
|
|
||||||
Log.d("Loki", "Failed to update device mapping")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both promises complete successfully then we should sync our contacts
|
|
||||||
all(listOf(sendPromise, updatePromise), cancelOthersOnError = false).success {
|
|
||||||
Log.d("Loki", "Successfully pairing with a secondary device! Syncing contacts.")
|
|
||||||
// Send out sync contact after a delay
|
|
||||||
Timer().schedule(3000) {
|
|
||||||
MessageSender.syncAllContacts(context, address)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +150,7 @@ fun isOneOfOurDevices(context: Context, address: Address): Promise<Boolean, Exce
|
|||||||
}
|
}
|
||||||
|
|
||||||
val ourPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val ourPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
return LokiStorageAPI.shared.getAllDevicePublicKeys(ourPublicKey).map { devices ->
|
return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(ourPublicKey).map { devices ->
|
||||||
devices.contains(address.serialize())
|
devices.contains(address.serialize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
|||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
import org.thoughtcrime.securesms.logging.Log
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.whispersystems.libsignal.util.guava.Optional
|
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
|
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||||
@ -99,7 +97,7 @@ class PushBackgroundMessageSendJob private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (message.get("sessionRestore", false)) {
|
if (message.get("sessionRestore", false)) {
|
||||||
dataMessage.asSessionRestore(true)
|
dataMessage.asSessionRestorationRequest(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.get("sessionRequest", false)) {
|
if (message.get("sessionRequest", false)) {
|
||||||
|
@ -104,9 +104,9 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
|
|||||||
val recipients = selectedMembers.map {
|
val recipients = selectedMembers.map {
|
||||||
Recipient.from(this, Address.fromSerialized(it), false)
|
Recipient.from(this, Address.fromSerialized(it), false)
|
||||||
}.toSet()
|
}.toSet()
|
||||||
val ourNumber = TextSecurePreferences.getMasterHexEncodedPublicKey(this) ?: TextSecurePreferences.getLocalNumber(this)
|
val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this) ?: TextSecurePreferences.getLocalNumber(this)
|
||||||
val local = Recipient.from(this, Address.fromSerialized(ourNumber), false)
|
val admin = Recipient.from(this, Address.fromSerialized(masterHexEncodedPublicKey), false)
|
||||||
CreateClosedGroupTask(WeakReference(this), null, name.toString(), recipients, setOf(local)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
CreateClosedGroupTask(WeakReference(this), null, name.toString(), recipients, setOf( admin )).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOpenConversation(threadId: Long, recipient: Recipient) {
|
private fun handleOpenConversation(threadId: Long, recipient: Recipient) {
|
||||||
@ -122,7 +122,7 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
|
|||||||
// region Tasks
|
// region Tasks
|
||||||
internal class CreateClosedGroupTask(
|
internal class CreateClosedGroupTask(
|
||||||
private val activity: WeakReference<CreateClosedGroupActivity>,
|
private val activity: WeakReference<CreateClosedGroupActivity>,
|
||||||
private val avatar: Bitmap?,
|
private val profilePicture: Bitmap?,
|
||||||
private val name: String?,
|
private val name: String?,
|
||||||
private val members: Set<Recipient>,
|
private val members: Set<Recipient>,
|
||||||
private val admins: Set<Recipient>
|
private val admins: Set<Recipient>
|
||||||
@ -130,24 +130,18 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
|
|||||||
|
|
||||||
override fun doInBackground(vararg params: Void?): Optional<GroupManager.GroupActionResult> {
|
override fun doInBackground(vararg params: Void?): Optional<GroupManager.GroupActionResult> {
|
||||||
val activity = activity.get() ?: return Optional.absent()
|
val activity = activity.get() ?: return Optional.absent()
|
||||||
return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false, admins))
|
return Optional.of(GroupManager.createGroup(activity, members, profilePicture, name, false, admins))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPostExecute(result: Optional<GroupManager.GroupActionResult>) {
|
override fun onPostExecute(result: Optional<GroupManager.GroupActionResult>) {
|
||||||
val activity = activity.get()
|
val activity = activity.get() ?: return super.onPostExecute(result)
|
||||||
if (activity == null) {
|
|
||||||
super.onPostExecute(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.isPresent && result.get().threadId > -1) {
|
if (result.isPresent && result.get().threadId > -1) {
|
||||||
if (!activity.isFinishing) {
|
if (!activity.isFinishing) {
|
||||||
activity.handleOpenConversation(result.get().threadId, result.get().groupRecipient)
|
activity.handleOpenConversation(result.get().threadId, result.get().groupRecipient)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
super.onPostExecute(result)
|
super.onPostExecute(result)
|
||||||
Toast.makeText(activity.applicationContext,
|
Toast.makeText(activity.applicationContext, "One of the members of your group has an invalid Session ID.", Toast.LENGTH_LONG).show()
|
||||||
R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ class CreateClosedGroupLoader(context: Context) : AsyncLoader<List<String>>(cont
|
|||||||
val threadDatabase = DatabaseFactory.getThreadDatabase(context)
|
val threadDatabase = DatabaseFactory.getThreadDatabase(context)
|
||||||
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
|
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
|
||||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getPairingAuthorisations(userHexEncodedPublicKey)
|
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(userHexEncodedPublicKey)
|
||||||
val userLinkedDeviceHexEncodedPublicKeys = deviceLinks.flatMap {
|
val userLinkedDeviceHexEncodedPublicKeys = deviceLinks.flatMap {
|
||||||
listOf( it.primaryDevicePublicKey.toLowerCase(), it.secondaryDevicePublicKey.toLowerCase() )
|
listOf( it.masterHexEncodedPublicKey.toLowerCase(), it.slaveHexEncodedPublicKey.toLowerCase() )
|
||||||
}.toMutableSet()
|
}.toMutableSet()
|
||||||
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase())
|
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase())
|
||||||
val cursor = threadDatabase.conversationList
|
val cursor = threadDatabase.conversationList
|
||||||
|
@ -4,7 +4,7 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_display_name_v2.*
|
import kotlinx.android.synthetic.main.activity_display_name.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
@ -19,7 +19,7 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setUpActionBarSessionLogo()
|
setUpActionBarSessionLogo()
|
||||||
setContentView(R.layout.activity_display_name_v2)
|
setContentView(R.layout.activity_display_name)
|
||||||
displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard
|
displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||||
registerButton.setOnClickListener { register() }
|
registerButton.setOnClickListener { register() }
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.ApplicationContext
|
|||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment
|
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment
|
||||||
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate
|
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
@ -72,6 +73,8 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
|||||||
val displayName = TextSecurePreferences.getProfileName(this)
|
val displayName = TextSecurePreferences.getProfileName(this)
|
||||||
val lokiPublicChatAPI = application.lokiPublicChatAPI!!
|
val lokiPublicChatAPI = application.lokiPublicChatAPI!!
|
||||||
application.lokiPublicChatManager.addChat(url, channel).successUi {
|
application.lokiPublicChatManager.addChat(url, channel).successUi {
|
||||||
|
DatabaseFactory.getLokiAPIDatabase(this).removeLastMessageServerID(channel, url)
|
||||||
|
DatabaseFactory.getLokiAPIDatabase(this).removeLastDeletionServerID(channel, url)
|
||||||
lokiPublicChatAPI.getMessages(channel, url)
|
lokiPublicChatAPI.getMessages(channel, url)
|
||||||
lokiPublicChatAPI.setDisplayName(displayName, url)
|
lokiPublicChatAPI.setDisplayName(displayName, url)
|
||||||
lokiPublicChatAPI.join(channel, url)
|
lokiPublicChatAPI.join(channel, url)
|
||||||
@ -133,7 +136,10 @@ class EnterChatURLFragment : Fragment() {
|
|||||||
private fun joinPublicChatIfPossible() {
|
private fun joinPublicChatIfPossible() {
|
||||||
val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
inputMethodManager.hideSoftInputFromWindow(chatURLEditText.windowToken, 0)
|
inputMethodManager.hideSoftInputFromWindow(chatURLEditText.windowToken, 0)
|
||||||
val chatURL = chatURLEditText.text.trim().toString().toLowerCase().replace("http://", "https://")
|
var chatURL = chatURLEditText.text.trim().toString().toLowerCase().replace("http://", "https://")
|
||||||
|
if (!chatURL.toLowerCase().startsWith("https")) {
|
||||||
|
chatURL = "https://$chatURL"
|
||||||
|
}
|
||||||
(activity!! as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
|
(activity!! as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.loki.redesign.dialogs.LinkDeviceSlaveModeDialo
|
|||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.push
|
import org.thoughtcrime.securesms.loki.redesign.utilities.push
|
||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
|
import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
|
||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.show
|
import org.thoughtcrime.securesms.loki.redesign.utilities.show
|
||||||
import org.thoughtcrime.securesms.loki.sendPairingAuthorisationMessage
|
import org.thoughtcrime.securesms.loki.sendDeviceLinkMessage
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
import org.thoughtcrime.securesms.util.Hex
|
import org.thoughtcrime.securesms.util.Hex
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
@ -26,7 +26,8 @@ import org.whispersystems.curve25519.Curve25519
|
|||||||
import org.whispersystems.libsignal.ecc.Curve
|
import org.whispersystems.libsignal.ecc.Curve
|
||||||
import org.whispersystems.libsignal.ecc.ECKeyPair
|
import org.whispersystems.libsignal.ecc.ECKeyPair
|
||||||
import org.whispersystems.libsignal.util.KeyHelper
|
import org.whispersystems.libsignal.util.KeyHelper
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
||||||
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
|
||||||
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
|
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
|
||||||
|
|
||||||
@ -92,11 +93,11 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
|||||||
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
|
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
|
||||||
TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
|
TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
|
||||||
TextSecurePreferences.setPromptedPushRegistration(this, true)
|
TextSecurePreferences.setPromptedPushRegistration(this, true)
|
||||||
val authorisation = PairingAuthorisation(hexEncodedPublicKey, userHexEncodedPublicKey).sign(PairingAuthorisation.Type.REQUEST, keyPair!!.privateKey.serialize())
|
val deviceLink = DeviceLink(hexEncodedPublicKey, userHexEncodedPublicKey).sign(DeviceLink.Type.REQUEST, keyPair!!.privateKey.serialize())
|
||||||
if (authorisation == null) {
|
if (deviceLink == null) {
|
||||||
Log.d("Loki", "Failed to sign device link request.")
|
Log.d("Loki", "Failed to sign device link request.")
|
||||||
reset()
|
reset()
|
||||||
return Toast.makeText(application, "Couldn't link device.", Toast.LENGTH_SHORT).show()
|
return Toast.makeText(application, "Couldn't link device.", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
val application = ApplicationContext.getInstance(this)
|
val application = ApplicationContext.getInstance(this)
|
||||||
application.startLongPollingIfNeeded()
|
application.startLongPollingIfNeeded()
|
||||||
@ -107,13 +108,14 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
|||||||
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
|
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
|
||||||
AsyncTask.execute {
|
AsyncTask.execute {
|
||||||
retryIfNeeded(8) {
|
retryIfNeeded(8) {
|
||||||
sendPairingAuthorisationMessage(this@LandingActivity, authorisation.primaryDevicePublicKey, authorisation)
|
sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterHexEncodedPublicKey, deviceLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) {
|
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
||||||
TextSecurePreferences.setMasterHexEncodedPublicKey(this, authorization.primaryDevicePublicKey)
|
LokiFileServerAPI.shared.addDeviceLink(deviceLink)
|
||||||
|
TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey)
|
||||||
val intent = Intent(this, HomeActivity::class.java)
|
val intent = Intent(this, HomeActivity::class.java)
|
||||||
show(intent)
|
show(intent)
|
||||||
finish()
|
finish()
|
||||||
|
@ -12,16 +12,21 @@ import android.view.View
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_linked_devices.*
|
import kotlinx.android.synthetic.main.activity_linked_devices.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import nl.komponents.kovenant.ui.failUi
|
||||||
|
import nl.komponents.kovenant.ui.successUi
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
import org.thoughtcrime.securesms.database.Address
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.devicelist.Device
|
import org.thoughtcrime.securesms.devicelist.Device
|
||||||
import org.thoughtcrime.securesms.loki.redesign.dialogs.*
|
import org.thoughtcrime.securesms.loki.redesign.dialogs.*
|
||||||
import org.thoughtcrime.securesms.loki.signAndSendPairingAuthorisationMessage
|
import org.thoughtcrime.securesms.loki.signAndSendDeviceLinkMessage
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.util.Util
|
import org.thoughtcrime.securesms.util.Util
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks<List<Device>>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate {
|
class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks<List<Device>>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate {
|
||||||
private var devices = listOf<Device>()
|
private var devices = listOf<Device>()
|
||||||
@ -118,20 +123,46 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
|
|||||||
private fun unlinkDevice(slaveDeviceHexEncodedPublicKey: String) {
|
private fun unlinkDevice(slaveDeviceHexEncodedPublicKey: String) {
|
||||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this)
|
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||||
val database = DatabaseFactory.getLokiAPIDatabase(this)
|
val database = DatabaseFactory.getLokiAPIDatabase(this)
|
||||||
database.removePairingAuthorisation(userHexEncodedPublicKey, slaveDeviceHexEncodedPublicKey)
|
val deviceLinks = database.getDeviceLinks(userHexEncodedPublicKey)
|
||||||
LokiStorageAPI.shared.updateUserDeviceMappings().success {
|
val deviceLink = deviceLinks.find { it.masterHexEncodedPublicKey == userHexEncodedPublicKey && it.slaveHexEncodedPublicKey == slaveDeviceHexEncodedPublicKey }
|
||||||
MessageSender.sendUnpairRequest(this, slaveDeviceHexEncodedPublicKey)
|
if (deviceLink == null) {
|
||||||
|
return Toast.makeText(this, "Couldn't unlink device.", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
LokiFileServerAPI.shared.setDeviceLinks(setOf()).successUi {
|
||||||
|
AsyncTask.execute {
|
||||||
|
DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userHexEncodedPublicKey)
|
||||||
|
deviceLinks.forEach { deviceLink ->
|
||||||
|
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
||||||
|
}
|
||||||
|
MessageSender.sendUnpairRequest(this, slaveDeviceHexEncodedPublicKey)
|
||||||
|
}
|
||||||
|
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
||||||
|
Toast.makeText(this, "Your device was unlinked successfully", Toast.LENGTH_LONG).show()
|
||||||
|
}.fail {
|
||||||
|
Toast.makeText(this, "Couldn't unlink device.", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
|
||||||
Toast.makeText(this, "Your device was unlinked successfully", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) {
|
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
||||||
AsyncTask.execute {
|
LokiFileServerAPI.shared.addDeviceLink(deviceLink).success {
|
||||||
signAndSendPairingAuthorisationMessage(this, authorization)
|
signAndSendDeviceLinkMessage(this, deviceLink).success {
|
||||||
Util.runOnMain {
|
TextSecurePreferences.setMultiDevice(this, true)
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
Util.runOnMain {
|
||||||
|
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
||||||
|
}
|
||||||
|
Timer().schedule(4000) {
|
||||||
|
MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey))
|
||||||
|
}
|
||||||
|
}.fail {
|
||||||
|
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
|
||||||
|
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
||||||
|
Util.runOnMain {
|
||||||
|
Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}.failUi {
|
||||||
|
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
||||||
|
Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.devicelist.Device
|
|||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
|
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities
|
||||||
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class LinkedDevicesLoader(context: Context) : AsyncLoader<List<Device>>(context)
|
|||||||
override fun loadInBackground(): List<Device>? {
|
override fun loadInBackground(): List<Device>? {
|
||||||
try {
|
try {
|
||||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val slaveDeviceHexEncodedPublicKeys = LokiStorageAPI.shared.getSecondaryDevicePublicKeys(userHexEncodedPublicKey).get()
|
val slaveDeviceHexEncodedPublicKeys = LokiDeviceLinkUtilities.getSlaveHexEncodedPublicKeys(userHexEncodedPublicKey).get()
|
||||||
return slaveDeviceHexEncodedPublicKeys.map { hexEncodedPublicKey ->
|
return slaveDeviceHexEncodedPublicKeys.map { hexEncodedPublicKey ->
|
||||||
val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey)
|
val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey)
|
||||||
val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)
|
val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)
|
||||||
|
@ -9,7 +9,7 @@ import android.text.SpannableString
|
|||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_seed_v2.*
|
import kotlinx.android.synthetic.main.activity_seed.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
@ -33,7 +33,7 @@ class SeedActivity : BaseActionBarActivity() {
|
|||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_seed_v2)
|
setContentView(R.layout.activity_seed)
|
||||||
supportActionBar!!.title = "Your Recovery Phrase"
|
supportActionBar!!.title = "Your Recovery Phrase"
|
||||||
val seedReminderViewTitle = SpannableString("You're almost finished! 90%")
|
val seedReminderViewTitle = SpannableString("You're almost finished! 90%")
|
||||||
seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil
|
|||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher
|
import org.whispersystems.signalservice.api.crypto.ProfileCipher
|
||||||
import org.whispersystems.signalservice.api.util.StreamDetails
|
import org.whispersystems.signalservice.api.util.StreamDetails
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
@ -159,7 +159,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
|
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
|
||||||
val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey)
|
val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey)
|
||||||
if (isUpdatingProfilePicture && profilePicture != null) {
|
if (isUpdatingProfilePicture && profilePicture != null) {
|
||||||
val storageAPI = LokiStorageAPI.shared
|
val storageAPI = LokiFileServerAPI.shared
|
||||||
val deferred = deferred<Unit, Exception>()
|
val deferred = deferred<Unit, Exception>()
|
||||||
AsyncTask.execute {
|
AsyncTask.execute {
|
||||||
val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong())
|
val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong())
|
||||||
@ -178,7 +178,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), profilePicture)
|
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), profilePicture)
|
||||||
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
||||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||||
ApplicationContext.getInstance(this).updatePublicChatProfileAvatarIfNeeded()
|
ApplicationContext.getInstance(this).updatePublicChatProfilePictureIfNeeded()
|
||||||
profilePictureView.update()
|
profilePictureView.update()
|
||||||
}
|
}
|
||||||
profilePictureToBeUploaded = null
|
profilePictureToBeUploaded = null
|
||||||
|
@ -17,15 +17,15 @@ import org.thoughtcrime.securesms.loki.redesign.utilities.QRCodeUtilities
|
|||||||
import org.thoughtcrime.securesms.loki.toPx
|
import org.thoughtcrime.securesms.loki.toPx
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.util.Util
|
import org.thoughtcrime.securesms.util.Util
|
||||||
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
|
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
|
||||||
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
|
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
|
||||||
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
||||||
|
|
||||||
class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener {
|
class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener {
|
||||||
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
|
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
|
||||||
private lateinit var contentView: View
|
private lateinit var contentView: View
|
||||||
private var authorization: PairingAuthorisation? = null
|
private var deviceLink: DeviceLink? = null
|
||||||
var delegate: LinkDeviceMasterModeDialogDelegate? = null
|
var delegate: LinkDeviceMasterModeDialogDelegate? = null
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
@ -45,10 +45,10 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requestUserAuthorization(authorization: PairingAuthorisation) {
|
override fun requestUserAuthorization(deviceLink: DeviceLink) {
|
||||||
if (authorization.type != PairingAuthorisation.Type.REQUEST || authorization.primaryDevicePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return }
|
if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
|
||||||
Util.runOnMain {
|
Util.runOnMain {
|
||||||
this.authorization = authorization
|
this.deviceLink = deviceLink
|
||||||
contentView.qrCodeImageView.visibility = View.GONE
|
contentView.qrCodeImageView.visibility = View.GONE
|
||||||
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
|
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
|
||||||
titleTextViewLayoutParams.topMargin = toPx(8, resources)
|
titleTextViewLayoutParams.topMargin = toPx(8, resources)
|
||||||
@ -56,13 +56,13 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
|
|||||||
contentView.titleTextView.text = "Linking Request Received"
|
contentView.titleTextView.text = "Linking Request Received"
|
||||||
contentView.explanationTextView.text = "Please check that the words below match those shown on your other device"
|
contentView.explanationTextView.text = "Please check that the words below match those shown on your other device"
|
||||||
contentView.mnemonicTextView.visibility = View.VISIBLE
|
contentView.mnemonicTextView.visibility = View.VISIBLE
|
||||||
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), authorization.secondaryDevicePublicKey)
|
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), deviceLink.slaveHexEncodedPublicKey)
|
||||||
contentView.authorizeButton.visibility = View.VISIBLE
|
contentView.authorizeButton.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun authorizeDeviceLink() {
|
private fun authorizeDeviceLink() {
|
||||||
val authorization = this.authorization ?: return
|
val authorization = this.deviceLink ?: return
|
||||||
delegate?.onDeviceLinkRequestAuthorized(authorization)
|
delegate?.onDeviceLinkRequestAuthorized(authorization)
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
DeviceLinkingSession.shared.removeListener(this)
|
||||||
@ -72,8 +72,8 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
|
|||||||
private fun onDeviceLinkCanceled() {
|
private fun onDeviceLinkCanceled() {
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
DeviceLinkingSession.shared.removeListener(this)
|
||||||
if (authorization != null) {
|
if (deviceLink != null) {
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorization!!.secondaryDevicePublicKey)
|
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slaveHexEncodedPublicKey)
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
delegate?.onDeviceLinkCanceled()
|
delegate?.onDeviceLinkCanceled()
|
||||||
@ -82,6 +82,6 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
|
|||||||
|
|
||||||
interface LinkDeviceMasterModeDialogDelegate {
|
interface LinkDeviceMasterModeDialogDelegate {
|
||||||
|
|
||||||
fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation)
|
fun onDeviceLinkRequestAuthorized(authorization: DeviceLink)
|
||||||
fun onDeviceLinkCanceled()
|
fun onDeviceLinkCanceled()
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,15 @@ import network.loki.messenger.R
|
|||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
|
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.util.Util
|
import org.thoughtcrime.securesms.util.Util
|
||||||
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
|
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
|
||||||
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
|
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
|
||||||
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
|
||||||
|
|
||||||
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
|
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
|
||||||
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
|
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
|
||||||
private lateinit var contentView: View
|
private lateinit var contentView: View
|
||||||
private var authorization: PairingAuthorisation? = null
|
private var deviceLink: DeviceLink? = null
|
||||||
var delegate: LinkDeviceSlaveModeDialogDelegate? = null
|
var delegate: LinkDeviceSlaveModeDialogDelegate? = null
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
@ -40,10 +40,10 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) {
|
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
||||||
if (authorization.type != PairingAuthorisation.Type.GRANT || authorization.secondaryDevicePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return }
|
if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return }
|
||||||
Util.runOnMain {
|
Util.runOnMain {
|
||||||
this.authorization = authorization
|
this.deviceLink = deviceLink
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
DeviceLinkingSession.shared.removeListener(this)
|
||||||
contentView.spinner.visibility = View.GONE
|
contentView.spinner.visibility = View.GONE
|
||||||
@ -56,7 +56,7 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
|
|||||||
contentView.cancelButton.visibility = View.GONE
|
contentView.cancelButton.visibility = View.GONE
|
||||||
Handler().postDelayed({
|
Handler().postDelayed({
|
||||||
dismiss()
|
dismiss()
|
||||||
delegate?.onDeviceLinkRequestAuthorized(authorization)
|
delegate?.onDeviceLinkRequestAuthorized(deviceLink)
|
||||||
}, 4000)
|
}, 4000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +71,6 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
|
|||||||
|
|
||||||
interface LinkDeviceSlaveModeDialogDelegate {
|
interface LinkDeviceSlaveModeDialogDelegate {
|
||||||
|
|
||||||
fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation)
|
fun onDeviceLinkRequestAuthorized(authorization: DeviceLink)
|
||||||
fun onDeviceLinkCanceled()
|
fun onDeviceLinkCanceled()
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
class BackgroundPollWorker : PersistentAlarmManagerListener() {
|
class BackgroundPollWorker : PersistentAlarmManagerListener() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val pollInterval = TimeUnit.MINUTES.toMillis(2)
|
private val pollInterval = TimeUnit.MINUTES.toMillis(1)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun schedule(context: Context) {
|
fun schedule(context: Context) {
|
||||||
|
@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
class BackgroundPublicChatPollWorker : PersistentAlarmManagerListener() {
|
class BackgroundPublicChatPollWorker : PersistentAlarmManagerListener() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val pollInterval = TimeUnit.MINUTES.toMillis(4)
|
private val pollInterval = TimeUnit.MINUTES.toMillis(2)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun schedule(context: Context) {
|
fun schedule(context: Context) {
|
||||||
|
@ -7,9 +7,9 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
|||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.*
|
import org.thoughtcrime.securesms.loki.redesign.utilities.*
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol
|
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol
|
||||||
import org.whispersystems.signalservice.loki.api.LokiAPITarget
|
import org.whispersystems.signalservice.loki.api.LokiAPITarget
|
||||||
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
|
|
||||||
|
|
||||||
// TODO: Clean this up a bit
|
// TODO: Clean this up a bit
|
||||||
|
|
||||||
@ -48,14 +48,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index"
|
private val lastDeletionServerIDCacheIndex = "loki_api_last_deletion_server_id_cache_index"
|
||||||
private val lastDeletionServerID = "last_deletion_server_id"
|
private val lastDeletionServerID = "last_deletion_server_id"
|
||||||
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
|
@JvmStatic val createLastDeletionServerIDTableCommand = "CREATE TABLE $lastDeletionServerIDCache ($lastDeletionServerIDCacheIndex STRING PRIMARY KEY, $lastDeletionServerID INTEGER DEFAULT 0);"
|
||||||
// Pairing authorisation cache
|
// Device link cache
|
||||||
private val pairingAuthorisationCache = "loki_pairing_authorisation_cache"
|
private val deviceLinkCache = "loki_pairing_authorisation_cache"
|
||||||
private val primaryDevicePublicKey = "primary_device"
|
private val masterHexEncodedPublicKey = "primary_device"
|
||||||
private val secondaryDevicePublicKey = "secondary_device"
|
private val slaveHexEncodedPublicKey = "secondary_device"
|
||||||
private val requestSignature = "request_signature"
|
private val requestSignature = "request_signature"
|
||||||
private val grantSignature = "grant_signature"
|
private val authorizationSignature = "grant_signature"
|
||||||
@JvmStatic val createPairingAuthorisationTableCommand = "CREATE TABLE $pairingAuthorisationCache ($primaryDevicePublicKey TEXT, $secondaryDevicePublicKey TEXT, " +
|
@JvmStatic val createDeviceLinkTableCommand = "CREATE TABLE $deviceLinkCache ($masterHexEncodedPublicKey TEXT, $slaveHexEncodedPublicKey TEXT, " +
|
||||||
"$requestSignature TEXT NULLABLE DEFAULT NULL, $grantSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($primaryDevicePublicKey, $secondaryDevicePublicKey));"
|
"$requestSignature TEXT NULLABLE DEFAULT NULL, $authorizationSignature TEXT NULLABLE DEFAULT NULL, PRIMARY KEY ($masterHexEncodedPublicKey, $slaveHexEncodedPublicKey));"
|
||||||
// User count cache
|
// User count cache
|
||||||
private val userCountCache = "loki_user_count_cache"
|
private val userCountCache = "loki_user_count_cache"
|
||||||
private val publicChatID = "public_chat_id"
|
private val publicChatID = "public_chat_id"
|
||||||
@ -179,35 +179,35 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index))
|
database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPairingAuthorisations(hexEncodedPublicKey: String): List<PairingAuthorisation> {
|
override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> {
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
|
return database.getAll(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
|
||||||
val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey)
|
val masterHexEncodedPublicKey = cursor.getString(masterHexEncodedPublicKey)
|
||||||
val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey)
|
val slaveHexEncodedPublicKey = cursor.getString(slaveHexEncodedPublicKey)
|
||||||
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
|
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
|
||||||
val grantSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(grantSignature))) null else cursor.getBase64EncodedData(grantSignature)
|
val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
|
||||||
PairingAuthorisation(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature)
|
DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
|
||||||
}
|
}.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun insertOrUpdatePairingAuthorisation(authorisation: PairingAuthorisation) {
|
override fun clearDeviceLinks(hexEncodedPublicKey: String) {
|
||||||
|
val database = databaseHelper.writableDatabase
|
||||||
|
database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addDeviceLink(deviceLink: DeviceLink) {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
values.put(primaryDevicePublicKey, authorisation.primaryDevicePublicKey)
|
values.put(masterHexEncodedPublicKey, deviceLink.masterHexEncodedPublicKey)
|
||||||
values.put(secondaryDevicePublicKey, authorisation.secondaryDevicePublicKey)
|
values.put(slaveHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey)
|
||||||
if (authorisation.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(authorisation.requestSignature)) }
|
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
|
||||||
if (authorisation.grantSignature != null) { values.put(grantSignature, Base64.encodeBytes(authorisation.grantSignature)) }
|
if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
|
||||||
database.insertOrUpdate(pairingAuthorisationCache, values, "$primaryDevicePublicKey = ? AND $secondaryDevicePublicKey = ?", arrayOf( authorisation.primaryDevicePublicKey, authorisation.secondaryDevicePublicKey ))
|
database.insertOrUpdate(deviceLinkCache, values, "$masterHexEncodedPublicKey = ? AND $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removePairingAuthorisations(hexEncodedPublicKey: String) {
|
override fun removeDeviceLink(deviceLink: DeviceLink) {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
database.delete(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
|
database.delete(deviceLinkCache, "$masterHexEncodedPublicKey = ? OR $slaveHexEncodedPublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
|
||||||
}
|
|
||||||
|
|
||||||
fun removePairingAuthorisation(primaryDevicePublicKey: String, secondaryDevicePublicKey: String) {
|
|
||||||
val database = databaseHelper.writableDatabase
|
|
||||||
database.delete(pairingAuthorisationCache, "${Companion.primaryDevicePublicKey} = ? OR ${Companion.secondaryDevicePublicKey} = ?", arrayOf( primaryDevicePublicKey, secondaryDevicePublicKey ))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserCount(group: Long, server: String): Int? {
|
fun getUserCount(group: Long, server: String): Int? {
|
||||||
|
@ -21,10 +21,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
|||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
|
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChat
|
import org.whispersystems.signalservice.loki.api.*
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI
|
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPublicChatMessage
|
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI
|
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
|
||||||
import org.whispersystems.signalservice.loki.utilities.successBackground
|
import org.whispersystems.signalservice.loki.utilities.successBackground
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
@ -155,36 +152,36 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
|||||||
|
|
||||||
fun pollForNewMessages() {
|
fun pollForNewMessages() {
|
||||||
fun processIncomingMessage(message: LokiPublicChatMessage) {
|
fun processIncomingMessage(message: LokiPublicChatMessage) {
|
||||||
// If the sender of the current message is not a secondary device, we need to set the display name in the database
|
// If the sender of the current message is not a slave device, set the display name in the database
|
||||||
val primaryDevice = LokiStorageAPI.shared.getPrimaryDevicePublicKey(message.hexEncodedPublicKey).get()
|
val masterHexEncodedPublicKey = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(message.hexEncodedPublicKey).get()
|
||||||
if (primaryDevice == null) {
|
if (masterHexEncodedPublicKey == null) {
|
||||||
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
|
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
|
||||||
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
|
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
|
||||||
}
|
}
|
||||||
val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey
|
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.hexEncodedPublicKey
|
||||||
val serviceDataMessage = getDataMessage(message)
|
val serviceDataMessage = getDataMessage(message)
|
||||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, 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) {
|
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))
|
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||||
} else {
|
} else {
|
||||||
PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||||
}
|
}
|
||||||
// Update profile avatar if needed
|
// Update profile picture if needed
|
||||||
val senderRecipient = Recipient.from(context, Address.fromSerialized(senderPublicKey), false)
|
val senderAsRecipient = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false)
|
||||||
if (message.avatar != null && message.avatar!!.url.isNotEmpty()) {
|
if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) {
|
||||||
val profileKey = message.avatar!!.profileKey
|
val profileKey = message.profilePicture!!.profileKey
|
||||||
val url = message.avatar!!.url
|
val url = message.profilePicture!!.url
|
||||||
if (senderRecipient.profileKey == null || !MessageDigest.isEqual(senderRecipient.profileKey, profileKey)) {
|
if (senderAsRecipient.profileKey == null || !MessageDigest.isEqual(senderAsRecipient.profileKey, profileKey)) {
|
||||||
val database = DatabaseFactory.getRecipientDatabase(context)
|
val database = DatabaseFactory.getRecipientDatabase(context)
|
||||||
database.setProfileKey(senderRecipient, profileKey)
|
database.setProfileKey(senderAsRecipient, profileKey)
|
||||||
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderRecipient, url))
|
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, url))
|
||||||
}
|
}
|
||||||
} else if (senderRecipient.profileAvatar.orEmpty().isNotEmpty()) {
|
} else if (senderAsRecipient.profileAvatar.orEmpty().isNotEmpty()) {
|
||||||
// Unset the avatar if we had an avatar before and we're not friends with the person
|
// 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(senderRecipient)
|
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(senderAsRecipient)
|
||||||
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId)
|
val friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID)
|
||||||
if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
|
if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
|
||||||
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderRecipient, ""))
|
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(senderAsRecipient, ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,25 +190,25 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
|||||||
val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
|
val isDuplicate = DatabaseFactory.getLokiMessageDatabase(context).getMessageID(messageServerID) != null
|
||||||
if (isDuplicate) { return }
|
if (isDuplicate) { return }
|
||||||
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
|
if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return }
|
||||||
val localNumber = TextSecurePreferences.getLocalNumber(context)
|
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val dataMessage = getDataMessage(message)
|
val dataMessage = getDataMessage(message)
|
||||||
val transcript = SentTranscriptMessage(localNumber, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(localNumber, false))
|
val transcript = SentTranscriptMessage(userHexEncodedPublicKey, dataMessage.timestamp, dataMessage, dataMessage.expiresInSeconds.toLong(), Collections.singletonMap(userHexEncodedPublicKey, false))
|
||||||
transcript.messageServerID = messageServerID
|
transcript.messageServerID = messageServerID
|
||||||
if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
|
if (dataMessage.quote.isPresent || (dataMessage.attachments.isPresent && dataMessage.attachments.get().size > 0) || dataMessage.previews.isPresent) {
|
||||||
PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript)
|
PushDecryptJob(context).handleSynchronizeSentMediaMessage(transcript)
|
||||||
} else {
|
} else {
|
||||||
PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript)
|
PushDecryptJob(context).handleSynchronizeSentTextMessage(transcript)
|
||||||
}
|
}
|
||||||
// If we got a message from our master device then make sure our mappings stay in sync
|
// 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.hexEncodedPublicKey), false)
|
||||||
if (recipient.isOurMasterDevice && message.avatar != null) {
|
if (recipient.isOurMasterDevice && message.profilePicture != null) {
|
||||||
val profileKey = message.avatar!!.profileKey
|
val profileKey = message.profilePicture!!.profileKey
|
||||||
val url = message.avatar!!.url
|
val url = message.profilePicture!!.url
|
||||||
if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profileKey)) {
|
if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profileKey)) {
|
||||||
val database = DatabaseFactory.getRecipientDatabase(context)
|
val database = DatabaseFactory.getRecipientDatabase(context)
|
||||||
database.setProfileKey(recipient, profileKey)
|
database.setProfileKey(recipient, profileKey)
|
||||||
database.setProfileAvatar(recipient, url)
|
database.setProfileAvatar(recipient, url)
|
||||||
ApplicationContext.getInstance(context).updatePublicChatProfileAvatarIfNeeded()
|
ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,29 +216,26 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
|||||||
var uniqueDevices = setOf<String>()
|
var uniqueDevices = setOf<String>()
|
||||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
||||||
val database = DatabaseFactory.getLokiAPIDatabase(context)
|
val database = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
LokiStorageAPI.configure(false, userHexEncodedPublicKey, userPrivateKey, database)
|
LokiFileServerAPI.configure(false, userHexEncodedPublicKey, userPrivateKey, database)
|
||||||
LokiStorageAPI.shared.getAllDevicePublicKeys(userHexEncodedPublicKey).bind { devices ->
|
LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(userHexEncodedPublicKey).bind { devices ->
|
||||||
userDevices = devices
|
userDevices = devices
|
||||||
api.getMessages(group.channel, group.server)
|
api.getMessages(group.channel, group.server)
|
||||||
}.bind { messages ->
|
}.bind { messages ->
|
||||||
if (messages.isNotEmpty()) {
|
if (messages.isNotEmpty()) {
|
||||||
// We need to fetch device mappings for all the devices we don't have
|
// We need to fetch the device mapping for any devices we don't have
|
||||||
uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet()
|
uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet()
|
||||||
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiStorageAPI.shared.hasCacheExpired(it) }
|
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) }
|
||||||
if (devicesToUpdate.isNotEmpty()) {
|
if (devicesToUpdate.isNotEmpty()) {
|
||||||
return@bind LokiStorageAPI.shared.getDeviceMappings(devicesToUpdate.toSet()).then { messages }
|
return@bind LokiFileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Promise.of(messages)
|
Promise.of(messages)
|
||||||
}.successBackground {
|
}.successBackground {
|
||||||
// Get the set of primary device pubKeys FROM the secondary devices in uniqueDevices
|
|
||||||
val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
|
val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
|
||||||
// This will return null if current device is primary
|
// This will return null if the current device is a master device
|
||||||
// So if it's non-null then we know the device is a secondary device
|
LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(it).get()
|
||||||
val primaryDevice = LokiStorageAPI.shared.getPrimaryDevicePublicKey(it).get()
|
|
||||||
primaryDevice
|
|
||||||
}.toSet()
|
}.toSet()
|
||||||
// Fetch the display names of the primary devices
|
// Fetch the display names of the master devices
|
||||||
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
|
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
|
||||||
}.successBackground { messages ->
|
}.successBackground { messages ->
|
||||||
// Process messages in the background
|
// Process messages in the background
|
||||||
|
@ -14,7 +14,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
|||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
import org.whispersystems.signalservice.loki.api.LokiRSSFeed
|
import org.whispersystems.signalservice.loki.api.LokiRSSFeed
|
||||||
import org.whispersystems.signalservice.loki.api.LokiRSSProxy
|
import org.whispersystems.signalservice.loki.api.LokiRSSFeedProxy
|
||||||
import org.whispersystems.signalservice.loki.utilities.successBackground
|
import org.whispersystems.signalservice.loki.utilities.successBackground
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@ -48,7 +48,7 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun poll() {
|
private fun poll() {
|
||||||
LokiRSSProxy.fetch(feed.url).successBackground { xml ->
|
LokiRSSFeedProxy.fetch(feed.url).successBackground { xml ->
|
||||||
val items = XMLParser(xml).call()
|
val items = XMLParser(xml).call()
|
||||||
items.reversed().forEach { item ->
|
items.reversed().forEach { item ->
|
||||||
val title = item.title ?: return@forEach
|
val title = item.title ?: return@forEach
|
||||||
|
@ -6,6 +6,11 @@ import android.support.v4.content.LocalBroadcastManager
|
|||||||
|
|
||||||
class Broadcaster(private val context: Context) : org.whispersystems.signalservice.loki.utilities.Broadcaster {
|
class Broadcaster(private val context: Context) : org.whispersystems.signalservice.loki.utilities.Broadcaster {
|
||||||
|
|
||||||
|
override fun broadcast(event: String) {
|
||||||
|
val intent = Intent(event)
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun broadcast(event: String, long: Long) {
|
override fun broadcast(event: String, long: Long) {
|
||||||
val intent = Intent(event)
|
val intent = Intent(event)
|
||||||
intent.putExtra("long", long)
|
intent.putExtra("long", long)
|
||||||
|
@ -53,7 +53,7 @@ object MentionUtilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = SpannableString(text)
|
val result = SpannableString(text)
|
||||||
val userLinkedDeviceHexEncodedPublicKeys = DatabaseFactory.getLokiAPIDatabase(context).getPairingAuthorisations(userHexEncodedPublicKey).flatMap { listOf( it.primaryDevicePublicKey, it.secondaryDevicePublicKey ) }.toMutableSet()
|
val userLinkedDeviceHexEncodedPublicKeys = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(userHexEncodedPublicKey).flatMap { listOf( it.masterHexEncodedPublicKey, it.slaveHexEncodedPublicKey ) }.toMutableSet()
|
||||||
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey)
|
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey)
|
||||||
for (mention in mentions) {
|
for (mention in mentions) {
|
||||||
if (!userLinkedDeviceHexEncodedPublicKeys.contains(mention.second)) { continue }
|
if (!userLinkedDeviceHexEncodedPublicKeys.contains(mention.second)) { continue }
|
||||||
|
@ -12,7 +12,7 @@ import network.loki.messenger.R
|
|||||||
import org.thoughtcrime.securesms.loki.getColorWithID
|
import org.thoughtcrime.securesms.loki.getColorWithID
|
||||||
import org.thoughtcrime.securesms.loki.toPx
|
import org.thoughtcrime.securesms.loki.toPx
|
||||||
|
|
||||||
class SeparatorView : RelativeLayout {
|
class LabeledSeparatorView : RelativeLayout {
|
||||||
|
|
||||||
private val path = Path()
|
private val path = Path()
|
||||||
|
|
@ -3,13 +3,12 @@ package org.thoughtcrime.securesms.mms;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
|
||||||
|
|
||||||
public class PushMediaConstraints extends MediaConstraints {
|
public class PushMediaConstraints extends MediaConstraints {
|
||||||
|
|
||||||
private static final int MAX_IMAGE_DIMEN_LOWMEM = 768;
|
private static final int MAX_IMAGE_DIMEN_LOWMEM = 768;
|
||||||
private static final int MAX_IMAGE_DIMEN = 4096;
|
private static final int MAX_IMAGE_DIMEN = 4096;
|
||||||
private static final int KB = 1024;
|
|
||||||
private static final int MB = 1024 * KB;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getImageMaxWidth(Context context) {
|
public int getImageMaxWidth(Context context) {
|
||||||
@ -23,26 +22,26 @@ public class PushMediaConstraints extends MediaConstraints {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getImageMaxSize(Context context) {
|
public int getImageMaxSize(Context context) {
|
||||||
return 6 * MB;
|
return LokiFileServerAPI.Companion.getMaxFileSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getGifMaxSize(Context context) {
|
public int getGifMaxSize(Context context) {
|
||||||
return 25 * MB;
|
return LokiFileServerAPI.Companion.getMaxFileSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getVideoMaxSize(Context context) {
|
public int getVideoMaxSize(Context context) {
|
||||||
return 100 * MB;
|
return LokiFileServerAPI.Companion.getMaxFileSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAudioMaxSize(Context context) {
|
public int getAudioMaxSize(Context context) {
|
||||||
return 100 * MB;
|
return LokiFileServerAPI.Companion.getMaxFileSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDocumentMaxSize(Context context) {
|
public int getDocumentMaxSize(Context context) {
|
||||||
return 100 * MB;
|
return LokiFileServerAPI.Companion.getMaxFileSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -9,14 +9,10 @@ fun getOpenGroupDisplayName(recipient: Recipient, threadRecipient: Recipient, co
|
|||||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(threadRecipient)
|
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(threadRecipient)
|
||||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||||
val hexEncodedPublicKey = recipient.address.toString()
|
val hexEncodedPublicKey = recipient.address.toString()
|
||||||
val displayName: String?
|
val displayName = if (publicChat != null) {
|
||||||
displayName = if (publicChat != null) {
|
|
||||||
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, hexEncodedPublicKey)
|
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, hexEncodedPublicKey)
|
||||||
} else {
|
} else {
|
||||||
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)
|
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)
|
||||||
}
|
}
|
||||||
if (displayName == null) {
|
return displayName ?: hexEncodedPublicKey
|
||||||
return hexEncodedPublicKey
|
|
||||||
}
|
|
||||||
return displayName
|
|
||||||
}
|
}
|
@ -38,7 +38,7 @@ public class MessageSenderEventListener implements SignalServiceMessageSender.Ev
|
|||||||
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Sent, messageID, threadID);
|
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Sent, messageID, threadID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void onFriendRequestSendingFail(long messageID, long threadID) {
|
@Override public void onFriendRequestSendingFailed(long messageID, long threadID) {
|
||||||
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Failed, messageID, threadID);
|
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Failed, messageID, threadID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ import org.thoughtcrime.securesms.util.Util;
|
|||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
|
||||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
||||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ public class MessageSender {
|
|||||||
sendBackgroundMessage(context, contactHexEncodedPublicKey);
|
sendBackgroundMessage(context, contactHexEncodedPublicKey);
|
||||||
|
|
||||||
// Go through the other devices and only send background messages if we're friends or we have received friend request
|
// Go through the other devices and only send background messages if we're friends or we have received friend request
|
||||||
LokiStorageAPI.shared.getAllDevicePublicKeys(contactHexEncodedPublicKey).success(devices -> {
|
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(contactHexEncodedPublicKey).success(devices -> {
|
||||||
Util.runOnMain(() -> {
|
Util.runOnMain(() -> {
|
||||||
for (String device : devices) {
|
for (String device : devices) {
|
||||||
// Don't send message to the device we already have sent to
|
// Don't send message to the device we already have sent to
|
||||||
@ -234,7 +234,7 @@ public class MessageSender {
|
|||||||
final int ttl) {
|
final int ttl) {
|
||||||
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
|
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||||
LokiStorageAPI.shared.getAllDevicePublicKeys(ourPublicKey).success(devices -> {
|
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(ourPublicKey).success(devices -> {
|
||||||
Util.runOnMain(() -> {
|
Util.runOnMain(() -> {
|
||||||
for (String device : devices) {
|
for (String device : devices) {
|
||||||
// Don't send to ourselves
|
// Don't send to ourselves
|
||||||
@ -306,8 +306,8 @@ public class MessageSender {
|
|||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||||
|
|
||||||
// Just send the message normally if it's a group message or we're sending to one of our devices
|
// Just send the message normally if it's a group message or we're sending to one of our devices
|
||||||
String recipientPublicKey = recipient.getAddress().serialize();
|
String recipientHexEncodedPublicKey = recipient.getAddress().serialize();
|
||||||
if (GeneralUtilitiesKt.isPublicChat(context, recipientPublicKey) || PromiseUtil.get(MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()), false)) {
|
if (GeneralUtilitiesKt.isPublicChat(context, recipientHexEncodedPublicKey) || PromiseUtil.get(MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()), false)) {
|
||||||
if (type == MessageType.MEDIA) {
|
if (type == MessageType.MEDIA) {
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
|
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false);
|
||||||
} else {
|
} else {
|
||||||
@ -318,16 +318,16 @@ public class MessageSender {
|
|||||||
|
|
||||||
// If we get here then we are sending a message to a device that is not ours
|
// If we get here then we are sending a message to a device that is not ours
|
||||||
boolean[] hasSentSyncMessage = { false };
|
boolean[] hasSentSyncMessage = { false };
|
||||||
MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientPublicKey).success(devices -> {
|
MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientHexEncodedPublicKey).success(devices -> {
|
||||||
int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet());
|
int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet());
|
||||||
Util.runOnMain(() -> {
|
Util.runOnMain(() -> {
|
||||||
ArrayList<Job> jobs = new ArrayList<>();
|
ArrayList<Job> jobs = new ArrayList<>();
|
||||||
for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
|
for (Map.Entry<String, Boolean> entry : devices.entrySet()) {
|
||||||
String devicePublicKey = entry.getKey();
|
String deviceHexEncodedPublicKey = entry.getKey();
|
||||||
boolean isFriend = entry.getValue();
|
boolean isFriend = entry.getValue();
|
||||||
|
|
||||||
Address address = Address.fromSerialized(devicePublicKey);
|
Address address = Address.fromSerialized(deviceHexEncodedPublicKey);
|
||||||
long messageIDToUse = recipientPublicKey.equals(devicePublicKey) ? messageId : -1L;
|
long messageIDToUse = recipientHexEncodedPublicKey.equals(deviceHexEncodedPublicKey) ? messageId : -1L;
|
||||||
|
|
||||||
if (isFriend) {
|
if (isFriend) {
|
||||||
// Send a normal message if the user is friends with the recipient
|
// Send a normal message if the user is friends with the recipient
|
||||||
@ -340,7 +340,7 @@ public class MessageSender {
|
|||||||
}
|
}
|
||||||
if (shouldSendSyncMessage) { hasSentSyncMessage[0] = true; }
|
if (shouldSendSyncMessage) { hasSentSyncMessage[0] = true; }
|
||||||
} else {
|
} else {
|
||||||
// Send friend requests to non friends. If the user is friends with any
|
// Send friend requests to non-friends. If the user is friends with any
|
||||||
// of the devices then send out a default friend request message.
|
// of the devices then send out a default friend request message.
|
||||||
boolean isFriendsWithAny = (friendCount > 0);
|
boolean isFriendsWithAny = (friendCount > 0);
|
||||||
String defaultFriendRequestMessage = isFriendsWithAny ? "Please accept to enable messages to be synced across devices" : null;
|
String defaultFriendRequestMessage = isFriendsWithAny ? "Please accept to enable messages to be synced across devices" : null;
|
||||||
|
@ -1235,11 +1235,11 @@ public class TextSecurePreferences {
|
|||||||
return getBooleanPreference(context, "database_reset_unpair", false);
|
return getBooleanPreference(context, "database_reset_unpair", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setNeedsRevocationCheck(Context context, boolean needsCheck) {
|
public static void setNeedsIsRevokedSlaveDeviceCheck(Context context, boolean value) {
|
||||||
setBooleanPreference(context, "needs_revocation", needsCheck);
|
setBooleanPreference(context, "needs_revocation", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean needsRevocationCheck(Context context) {
|
public static boolean getNeedsIsRevokedSlaveDeviceCheck(Context context) {
|
||||||
return getBooleanPreference(context, "needs_revocation", false);
|
return getBooleanPreference(context, "needs_revocation", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user