Merge branch 'dev'

This commit is contained in:
nielsandriesse 2020-05-26 16:05:45 +10:00
commit 4001714705
245 changed files with 3483 additions and 4334 deletions

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ jni/libspeex/.deps/
pkcs11.password pkcs11.password
fabric.properties fabric.properties
play play
google-services.json

View File

@ -16,12 +16,6 @@
<uses-feature <uses-feature
android:name="android.hardware.bluetooth" android:name="android.hardware.bluetooth"
android:required="false" /> android:required="false" />
<!--
Loki - Enable again once we have location sharing
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
-->
<uses-feature <uses-feature
android:name="android.hardware.microphone" android:name="android.hardware.microphone"
android:required="false" /> android:required="false" />
@ -56,8 +50,6 @@
<!-- For conversation 'shortcuts' on the desktop --> <!-- For conversation 'shortcuts' on the desktop -->
<uses-permission android:name="android.permission.INSTALL_SHORTCUT" /> <uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- Set image as wallpaper -->
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
@ -96,62 +88,62 @@
<!-- Session --> <!-- Session -->
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.LandingActivity" android:name="org.thoughtcrime.securesms.loki.activities.LandingActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.RegisterActivity" android:name="org.thoughtcrime.securesms.loki.activities.RegisterActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.RestoreActivity" android:name="org.thoughtcrime.securesms.loki.activities.RestoreActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.LinkDeviceActivity" android:name="org.thoughtcrime.securesms.loki.activities.LinkDeviceActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.DisplayNameActivity" android:name="org.thoughtcrime.securesms.loki.activities.DisplayNameActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.PNModeActivity" android:name="org.thoughtcrime.securesms.loki.activities.PNModeActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity" android:name="org.thoughtcrime.securesms.loki.activities.HomeActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/Session.DarkTheme.NoActionBar" /> android:theme="@style/Session.DarkTheme.NoActionBar" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.SettingsActivity" android:name="org.thoughtcrime.securesms.loki.activities.SettingsActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Session.DarkTheme.NoActionBar" /> android:theme="@style/Session.DarkTheme.NoActionBar" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.QRCodeActivity" android:name="org.thoughtcrime.securesms.loki.activities.QRCodeActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.CreatePrivateChatActivity" android:name="org.thoughtcrime.securesms.loki.activities.CreatePrivateChatActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.CreateClosedGroupActivity" android:name="org.thoughtcrime.securesms.loki.activities.CreateClosedGroupActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.JoinPublicChatActivity" android:name="org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.SeedActivity" android:name="org.thoughtcrime.securesms.loki.activities.SeedActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.PrivacySettingsActivity" android:name="org.thoughtcrime.securesms.loki.activities.PrivacySettingsActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.NotificationSettingsActivity" android:name="org.thoughtcrime.securesms.loki.activities.NotificationSettingsActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.ChatSettingsActivity" android:name="org.thoughtcrime.securesms.loki.activities.ChatSettingsActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.LinkedDevicesActivity" android:name="org.thoughtcrime.securesms.loki.activities.LinkedDevicesActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<!-- Session --> <!-- Session -->
<activity <activity
@ -166,7 +158,7 @@
<activity <activity
android:name="org.thoughtcrime.securesms.InviteActivity" android:name="org.thoughtcrime.securesms.InviteActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:parentActivityName="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity" android:parentActivityName="org.thoughtcrime.securesms.loki.activities.HomeActivity"
android:theme="@style/TextSecure.HighlightTheme" android:theme="@style/TextSecure.HighlightTheme"
android:windowSoftInputMode="stateHidden"> android:windowSoftInputMode="stateHidden">
<meta-data <meta-data
@ -225,7 +217,7 @@
<activity-alias <activity-alias
android:name=".RoutingActivity" android:name=".RoutingActivity"
android:exported="true" android:exported="true"
android:targetActivity="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity"> android:targetActivity="org.thoughtcrime.securesms.loki.activities.HomeActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -264,7 +256,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:label="@string/AndroidManifest_archived_conversations" android:label="@string/AndroidManifest_archived_conversations"
android:launchMode="singleTask" android:launchMode="singleTask"
android:parentActivityName="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity"> android:parentActivityName="org.thoughtcrime.securesms.loki.activities.HomeActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="PsiClass:HomeActivity" /> android:value="PsiClass:HomeActivity" />
@ -275,7 +267,7 @@
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Session.DarkTheme.NoActionBar" android:theme="@style/Session.DarkTheme.NoActionBar"
android:parentActivityName="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity" android:parentActivityName="org.thoughtcrime.securesms.loki.activities.HomeActivity"
android:windowSoftInputMode="stateUnchanged"> android:windowSoftInputMode="stateUnchanged">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
@ -514,7 +506,7 @@
android:exported="true" android:exported="true"
android:theme="@style/TextSecure.LightNoActionBar" /> android:theme="@style/TextSecure.LightNoActionBar" />
<service <service
android:name="org.thoughtcrime.securesms.service.PushNotificationService" android:name="org.thoughtcrime.securesms.loki.api.PushNotificationService"
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@ -673,12 +665,12 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- Session --> <!-- Session -->
<receiver android:name="org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundPollWorker"> <receiver android:name="org.thoughtcrime.securesms.loki.api.BackgroundPollWorker">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name="org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundOpenGroupPollWorker"> <receiver android:name="org.thoughtcrime.securesms.loki.api.BackgroundOpenGroupPollWorker">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>

View File

@ -190,8 +190,8 @@ dependencies {
implementation "com.github.ybq:Android-SpinKit:1.4.0" implementation "com.github.ybq:Android-SpinKit:1.4.0"
} }
def canonicalVersionCode = 49 def canonicalVersionCode = 50
def canonicalVersionName = "1.1.0" def canonicalVersionName = "1.2.0"
def postFixSize = 10 def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1, def abiPostFix = ['armeabi-v7a' : 1,

View File

@ -21,7 +21,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:text="Your Session begins here..." /> android:text="Your Session begins here..." />
<org.thoughtcrime.securesms.loki.redesign.views.FakeChatView <org.thoughtcrime.securesms.loki.views.FakeChatView
android:id="@+id/fakeChatView" android:id="@+id/fakeChatView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/fake_chat_view_height" android:layout_height="@dimen/fake_chat_view_height"

View File

@ -7,7 +7,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView <org.thoughtcrime.securesms.loki.views.SeedReminderView
android:id="@+id/seedReminderView" android:id="@+id/seedReminderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@ -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 &quot;Share Session ID&quot;, or by sharing their QR code." /> android:text="Users can share their Session ID by going into their account settings and tapping &quot;Share Session ID&quot;, or by sharing their QR code." />
<org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView <org.thoughtcrime.securesms.loki.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"

View File

@ -0,0 +1,75 @@
<?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="wrap_content"
android:background="@color/cell_background"
android:orientation="vertical">
<ProgressBar
style="@android:style/Widget.ProgressBar.Horizontal"
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="@dimen/accent_line_thickness"
android:paddingLeft="-2dp"
android:paddingRight="-2dp"
android:progressTint="@color/accent"
android:progressBackgroundTint="@color/progress_bar_background"
android:progress="80" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="@dimen/medium_spacing"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textStyle="bold"
android:text="You're almost finished! 80%" />
<TextView
android:id="@+id/subtitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="@color/text"
android:textSize="@dimen/very_small_font_size"
android:alpha="0.6"
android:text="Secure your account by saving your recovery phrase" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginLeft="4dp" />
<Button
style="@style/MediumProminentOutlineButton"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="28dp"
android:layout_marginLeft="4dp"
android:textStyle="normal"
android:text="Continue" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/separator" />
</LinearLayout>

View File

@ -25,7 +25,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="6dp" android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
@ -37,7 +37,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="12dp" android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:hint="Enter a display name" /> android:hint="Enter a display name" />

View File

@ -21,11 +21,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profileButton" android:id="@+id/profileButton"
android:layout_width="@dimen/small_profile_picture_size" android:layout_width="@dimen/small_profile_picture_size"
android:layout_height="@dimen/small_profile_picture_size" android:layout_height="@dimen/small_profile_picture_size"
android:layout_marginLeft="10dp" android:layout_marginLeft="9dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_centerVertical="true" /> android:layout_centerVertical="true" />
@ -44,7 +44,7 @@
</android.support.v7.widget.Toolbar> </android.support.v7.widget.Toolbar>
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView <org.thoughtcrime.securesms.loki.views.SeedReminderView
android:id="@+id/seedReminderView" android:id="@+id/seedReminderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@ -66,13 +66,39 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/home_activity_gradient" /> android:background="@drawable/home_activity_gradient" />
<org.thoughtcrime.securesms.loki.redesign.views.NewConversationButtonSetView <org.thoughtcrime.securesms.loki.views.NewConversationButtonSetView
android:id="@+id/newConversationButtonSet" android:id="@+id/newConversationButtonSet"
android:layout_width="252dp" android:layout_width="252dp"
android:layout_height="212dp" android:layout_height="212dp"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true" /> android:layout_alignParentBottom="true" />
<LinearLayout
android:id="@+id/emptyStateContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="32dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_centerInParent="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:text="You don't have any contacts yet" />
<Button
style="@style/MediumProminentOutlineButton"
android:id="@+id/createNewPrivateChatButton"
android:layout_width="196dp"
android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/medium_spacing"
android:text="Start a Session" />
</LinearLayout>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View File

@ -21,7 +21,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:text="Your Session begins here..." /> android:text="Your Session begins here..." />
<org.thoughtcrime.securesms.loki.redesign.views.FakeChatView <org.thoughtcrime.securesms.loki.views.FakeChatView
android:id="@+id/fakeChatView" android:id="@+id/fakeChatView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/fake_chat_view_height" android:layout_height="@dimen/fake_chat_view_height"

View File

@ -31,7 +31,7 @@
android:layout_width="196dp" android:layout_width="196dp"
android:layout_height="@dimen/medium_button_height" android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/medium_spacing" android:layout_marginTop="@dimen/medium_spacing"
android:text="Link a Device" /> android:text="Link a Device (Beta)" />
</LinearLayout> </LinearLayout>

View File

@ -25,7 +25,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="6dp" android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
@ -38,7 +38,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18dp" android:textSize="18dp"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="12dp" android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />

View File

@ -25,7 +25,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="6dp" android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
@ -37,7 +37,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="12dp" android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:hint="Enter your recovery phrase" /> android:hint="Enter your recovery phrase" />

View File

@ -7,7 +7,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView <org.thoughtcrime.securesms.loki.views.SeedReminderView
android:id="@+id/seedReminderView" android:id="@+id/seedReminderView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@ -22,7 +22,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/large_font_size" android:textSize="19sp"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Meet your recovery phrase" /> android:text="Meet your recovery phrase" />
@ -31,9 +31,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="10dp" android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/medium_font_size" android:textSize="14sp"
android:textColor="@color/text" 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 dont give it to anyone. To restore your Session ID, launch Session and tap Continue your Session." /> 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 dont give it to anyone. To restore your Session ID, launch Session and tap Continue your Session." />
@ -43,7 +43,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="@dimen/medium_spacing" android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:gravity="center" android:gravity="center"
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
@ -54,9 +54,9 @@
android:id="@+id/revealButton" android:id="@+id/revealButton"
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="6dp"
android:textAlignment="center" android:textAlignment="center"
android:textSize="@dimen/medium_font_size" android:textSize="14sp"
android:textColor="@color/text" android:textColor="@color/text"
android:alpha="0.6" android:alpha="0.6"
android:text="Hold to reveal" /> android:text="Hold to reveal" />

View File

@ -70,7 +70,7 @@
</android.support.v7.widget.Toolbar> </android.support.v7.widget.Toolbar>
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView" android:id="@+id/profilePictureView"
android:layout_width="@dimen/large_profile_picture_size" android:layout_width="@dimen/large_profile_picture_size"
android:layout_height="@dimen/large_profile_picture_size" android:layout_height="@dimen/large_profile_picture_size"
@ -107,7 +107,7 @@
</RelativeLayout> </RelativeLayout>
<org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView <org.thoughtcrime.securesms.loki.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"

View File

@ -19,6 +19,6 @@
<fragment android:id="@+id/contact_selection_list_fragment" <fragment android:id="@+id/contact_selection_list_fragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment" /> android:name="org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment" />
</LinearLayout> </LinearLayout>

View File

@ -1,92 +1,53 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout
android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent" android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView <LinearLayout
android:id="@+id/recycler_view" android:id="@+id/emptyStateContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="vertical" />
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center|center_vertical"
android:layout_marginTop="15dp"
android:text="@string/contact_selection_group_activity__finding_contacts"
android:textSize="20sp" />
</android.support.v4.widget.SwipeRefreshLayout>
<org.thoughtcrime.securesms.components.RecyclerViewFastScroller
android:id="@+id/fast_scroller"
android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone" android:gravity="center"
android:layout_gravity="end"/> android:orientation="vertical">
<LinearLayout android:id="@+id/show_contacts_container" <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:text="You don't have any contacts yet"
android:layout_gravity="center" android:textColor="@color/text"
android:visibility="gone" android:textSize="@dimen/medium_font_size" />
tools:visibility="visible">
<FrameLayout </LinearLayout>
<LinearLayout
android:id="@+id/mainContentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="vertical" />
<com.pnikosis.materialishprogress.ProgressWheel <TextView
android:id="@+id/progress" android:id="@+id/loadingTextView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="invisible" android:gravity="center"
app:matProg_circleRadius="145dp" android:textColor="@color/text"
app:matProg_barWidth="6dp" android:text="@string/contact_selection_group_activity__finding_contacts"
app:matProg_rimColor="@color/signal_primary" android:textSize="@dimen/large_font_size" />
app:matProg_barColor="@color/signal_primary_dark"
app:matProg_progressIndeterminate="true"
tools:visibility="visible"
/>
<ImageView android:layout_gravity="center" </android.support.v4.widget.SwipeRefreshLayout>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/no_contacts"/>
</FrameLayout>
<TextView android:id="@+id/show_contacts_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:textSize="15sp"
android:lineSpacingMultiplier="1.3"
android:gravity="center"
android:text="@string/contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them"/>
<Button android:id="@+id/show_contacts_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
android:background="@color/signal_primary"
android:textColor="@color/white"
android:padding="10dp"
android:text="@string/contact_selection_list_fragment__show_contacts"/>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>

View File

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.contacts.ContactSelectionListItem
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:focusable="true"
android:background="@drawable/conversation_item_background"
android:paddingStart="16dp"
android:paddingEnd="8dp">
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView
android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" />
<LinearLayout android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:paddingEnd="16dp"
android:gravity="center_vertical"
android:orientation="vertical">
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:singleLine="true"
android:ellipsize="marquee"
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:textStyle="bold"
tools:text="Frieeeeeeedrich Nieeeeeeeeeetzsche" />
<LinearLayout android:id="@+id/number_container"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone">
<TextView android:id="@+id/number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textDirection="ltr"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="14sp"
android:fontFamily="sans-serif-light"
tools:text="+1 (555) 555-5555" />
<TextView android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dip"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:fontFamily="sans-serif-light"
tools:text="Mobile" />
</LinearLayout>
</LinearLayout>
<CheckBox android:id="@+id/check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false" />
</org.thoughtcrime.securesms.contacts.ContactSelectionListItem>

View File

@ -104,7 +104,7 @@
android:indeterminate="false" android:indeterminate="false"
android:progress="0" /> android:progress="0" />
<org.thoughtcrime.securesms.loki.redesign.views.SessionRestoreBannerView <org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView
android:id="@+id/sessionRestoreBannerView" android:id="@+id/sessionRestoreBannerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@ -28,7 +28,7 @@
android:layout_height="42dp" android:layout_height="42dp"
android:layout_alignParentStart="true"> android:layout_alignParentStart="true">
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView" android:id="@+id/profilePictureView"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:layout_width="@dimen/small_profile_picture_size" android:layout_width="@dimen/small_profile_picture_size"
@ -208,7 +208,7 @@
</LinearLayout> </LinearLayout>
<org.thoughtcrime.securesms.loki.redesign.views.FriendRequestView <org.thoughtcrime.securesms.loki.views.FriendRequestView
android:id="@+id/friend_request_view" android:id="@+id/friend_request_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -18,7 +18,7 @@
android:id="@+id/container" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/large_spacing" android:layout_marginEnd="12dp"
android:clipToPadding="false" android:clipToPadding="false"
android:clipChildren="false"> android:clipChildren="false">
@ -161,7 +161,7 @@
</LinearLayout> </LinearLayout>
<org.thoughtcrime.securesms.loki.redesign.views.FriendRequestView <org.thoughtcrime.securesms.loki.views.FriendRequestView
android:id="@+id/friend_request_view" android:id="@+id/friend_request_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -4,7 +4,7 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="@dimen/large_spacing" android:paddingStart="12dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingBottom="@dimen/small_spacing" android:paddingBottom="@dimen/small_spacing"
android:gravity="center_vertical"> android:gravity="center_vertical">

View File

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/default_dialog_background_inset" android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
@ -12,6 +13,7 @@
android:paddingBottom="@dimen/medium_spacing"> android:paddingBottom="@dimen/medium_spacing">
<RelativeLayout <RelativeLayout
android:id="@+id/qrCodeImageViewContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/small_spacing" android:layout_marginTop="@dimen/small_spacing"
@ -24,6 +26,15 @@
</RelativeLayout> </RelativeLayout>
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.DoubleBounce"
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:visibility="gone"
app:SpinKit_Color="@color/text" />
<TextView <TextView
android:id="@+id/titleTextView" android:id="@+id/titleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -56,6 +67,7 @@
android:visibility="gone" /> android:visibility="gone" />
<LinearLayout <LinearLayout
android:id="@+id/buttonContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing" android:layout_marginTop="@dimen/large_spacing"

View File

@ -18,7 +18,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_centerInParent="true"
app:SpinKit_Color="@color/text" /> app:SpinKit_Color="@color/text" />
<TextView <TextView

View File

@ -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 &quot;Share Session ID&quot;, or by sharing their QR code." /> android:text="Users can share their Session ID by going into their account settings and tapping &quot;Share Session ID&quot;, or by sharing their QR code." />
<org.thoughtcrime.securesms.loki.redesign.views.LabeledSeparatorView <org.thoughtcrime.securesms.loki.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"

View File

@ -26,7 +26,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/large_spacing" android:layout_marginLeft="@dimen/large_spacing"
android:layout_marginTop="6dp" android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/large_spacing" android:layout_marginRight="@dimen/large_spacing"
android:textSize="14sp" android:textSize="14sp"
android:textColor="@color/text" android:textColor="@color/text"
@ -38,7 +38,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/large_spacing" android:layout_marginLeft="@dimen/large_spacing"
android:layout_marginTop="12dp" android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/large_spacing" android:layout_marginRight="@dimen/large_spacing"
android:hint="Enter your session ID" /> android:hint="Enter your session ID" />

View File

@ -94,7 +94,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment" android:name="org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment"
tools:layout="@layout/contact_selection_list_fragment"/> tools:layout="@layout/contact_selection_list_fragment"/>
<LinearLayout android:layout_width="match_parent" <LinearLayout android:layout_width="match_parent"

View File

@ -49,7 +49,7 @@
android:layout_below="@id/toolbar" android:layout_below="@id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment" /> android:name="org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment" />
<org.thoughtcrime.securesms.components.SearchToolbar <org.thoughtcrime.securesms.components.SearchToolbar
android:id="@+id/search_toolbar" android:id="@+id/search_toolbar"

View File

@ -13,7 +13,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/accent" /> android:background="@color/accent" />
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView" android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size" android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" android:layout_height="@dimen/medium_profile_picture_size"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.loki.redesign.views.MentionCandidateView <org.thoughtcrime.securesms.loki.views.MentionCandidateView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="44dp" android:layout_height="44dp"
@ -13,7 +13,7 @@
android:layout_width="26dp" android:layout_width="26dp"
android:layout_height="32dp"> android:layout_height="32dp">
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView" android:id="@+id/profilePictureView"
android:layout_width="@dimen/very_small_profile_picture_size" android:layout_width="@dimen/very_small_profile_picture_size"
android:layout_height="@dimen/very_small_profile_picture_size" android:layout_height="@dimen/very_small_profile_picture_size"
@ -39,4 +39,4 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" /> android:ellipsize="end" />
</org.thoughtcrime.securesms.loki.redesign.views.MentionCandidateView> </org.thoughtcrime.securesms.loki.views.MentionCandidateView>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.loki.redesign.views.MentionCandidateSelectionView <org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/userSelectionView" android:id="@+id/userSelectionView"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/cell_background" android:background="@color/cell_background"
@ -39,11 +40,12 @@
<TextView <TextView
android:id="@+id/subtitleTextView" android:id="@+id/subtitleTextView"
android:layout_width="wrap_content" android:layout_width="224dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:lines="2"
android:alpha="0.6" android:alpha="0.6"
android:text="Secure your account by saving your recovery phrase" /> android:text="Secure your account by saving your recovery phrase" />
@ -52,14 +54,15 @@
<View <View
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" /> android:layout_weight="1"
android:layout_marginLeft="4dp" />
<Button <Button
style="@style/MediumProminentOutlineButton" style="@style/MediumProminentOutlineButton"
android:id="@+id/button" android:id="@+id/button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="27dp" android:layout_height="28dp"
android:layout_marginLeft="@dimen/small_spacing" android:layout_marginLeft="4dp"
android:textStyle="normal" android:textStyle="normal"
android:text="Continue" /> android:text="Continue" />

View File

@ -14,7 +14,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:padding="@dimen/medium_spacing"> android:padding="@dimen/medium_spacing">
<org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView" android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size" android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" /> android:layout_height="@dimen/medium_profile_picture_size" />

View File

@ -1663,13 +1663,14 @@
<string name="session_restore_banner_dismiss_button_title">Dismiss</string> <string name="session_restore_banner_dismiss_button_title">Dismiss</string>
<string name="session_restore_banner_restore_button_title">Restore</string> <string name="session_restore_banner_restore_button_title">Restore</string>
<!-- Loki -->
<!-- Session --> <!-- Session -->
<string name="activity_register_public_key_copied_message">Copied to clipboard</string> <string name="activity_register_public_key_copied_message">Copied to clipboard</string>
<string name="activity_home_leave_group_dialog_message">Are you sure you want to leave this group?</string> <string name="activity_home_leave_group_dialog_message">Are you sure you want to leave this group?</string>
<string name="activity_home_delete_conversation_dialog_message">Are you sure you want to delete this conversation?</string> <string name="activity_home_delete_conversation_dialog_message">Are you sure you want to delete this conversation?</string>
<string name="activity_home_conversation_deleted_message">Conversation deleted</string> <string name="activity_home_conversation_deleted_message">Conversation deleted</string>
<string name="fragment_contact_selection_contacts_title">Contacts</string>
<string name="fragment_contact_selection_closed_groups_title">Closed Groups</string>
<string name="fragment_contact_selection_open_groups_title">Open Groups</string>
<string name="activity_pn_mode_title">Push Notifications</string> <string name="activity_pn_mode_title">Push Notifications</string>
<string name="activity_pn_mode_explanation">There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.</string> <string name="activity_pn_mode_explanation">There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.</string>
<string name="activity_pn_mode_fcm_option_title">Firebase Cloud Messaging</string> <string name="activity_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<network-security-config> <network-security-config>
<domain-config cleartextTrafficPermitted="true"> <domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">149.56.148.124</domain> <domain includeSubdomains="true">144.76.164.202</domain>
<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</domain> <domain includeSubdomains="true">public.loki.foundation</domain>

View File

@ -19,10 +19,8 @@ package org.thoughtcrime.securesms;
import android.arch.lifecycle.DefaultLifecycleObserver; import android.arch.lifecycle.DefaultLifecycleObserver;
import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.ProcessLifecycleOwner; import android.arch.lifecycle.ProcessLifecycleOwner;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.ContentObserver;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
@ -41,12 +39,10 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule; import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.jobmanager.DependencyInjector; import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer; import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
@ -62,16 +58,18 @@ import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.logging.PersistentLogger; import org.thoughtcrime.securesms.logging.PersistentLogger;
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger; import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
import org.thoughtcrime.securesms.loki.LokiPublicChatManager; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.LokiPushNotificationManager; import org.thoughtcrime.securesms.loki.api.BackgroundOpenGroupPollWorker;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager;
import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundOpenGroupPollWorker; import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundPollWorker; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiRSSFeedPoller; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase; import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
import org.thoughtcrime.securesms.loki.redesign.utilities.Broadcaster; import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.AvatarHelper;
@ -86,6 +84,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory;
import org.webrtc.PeerConnectionFactory.InitializationOptions; import org.webrtc.PeerConnectionFactory.InitializationOptions;
@ -96,25 +95,29 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.api.LokiAPI;
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
import org.whispersystems.signalservice.loki.api.LokiPoller; import org.whispersystems.signalservice.loki.api.LokiPoller;
import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement; import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement;
import org.whispersystems.signalservice.loki.api.LokiSwarmAPI; import org.whispersystems.signalservice.loki.api.LokiSwarmAPI;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.p2p.LokiP2PAPI;
import org.whispersystems.signalservice.loki.api.rssfeeds.LokiRSSFeed; import org.whispersystems.signalservice.loki.api.p2p.LokiP2PAPIDelegate;
import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol;
import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink;
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol;
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate;
import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.Security; import java.security.Security;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import dagger.ObjectGraph; import dagger.ObjectGraph;
@ -132,7 +135,7 @@ import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
* *
* @author Moxie Marlinspike * @author Moxie Marlinspike
*/ */
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate { public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate, SessionManagementProtocolDelegate {
private static final String TAG = ApplicationContext.class.getSimpleName(); private static final String TAG = ApplicationContext.class.getSimpleName();
private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB
@ -146,10 +149,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
private PersistentLogger persistentLogger; private PersistentLogger persistentLogger;
// Loki // Loki
private LokiPoller lokiPoller = null; public LokiPoller lokiPoller = null;
private LokiRSSFeedPoller lokiNewsFeedPoller = null; public LokiPublicChatManager lokiPublicChatManager = null;
private LokiRSSFeedPoller lokiMessengerUpdatesFeedPoller = null;
private LokiPublicChatManager lokiPublicChatManager = null;
private LokiPublicChatAPI lokiPublicChatAPI = null; private LokiPublicChatAPI lokiPublicChatAPI = null;
public Broadcaster broadcaster = null; public Broadcaster broadcaster = null;
public SignalCommunicationModule communicationModule; public SignalCommunicationModule communicationModule;
@ -164,13 +165,42 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Log.i(TAG, "onCreate()"); Log.i(TAG, "onCreate()");
broadcaster = new Broadcaster(this);
checkNeedsDatabaseReset();
startKovenant(); startKovenant();
initializeSecurityProvider(); initializeSecurityProvider();
initializeLogging(); initializeLogging();
initializeCrashHandling(); initializeCrashHandling();
initializeDependencyInjection(); initializeDependencyInjection();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
// Loki
// ========
broadcaster = new Broadcaster(this);
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
LokiSessionResetImplementation sessionResetImpl = new LokiSessionResetImplementation(this);
if (userPublicKey != null) {
FriendRequestProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
}
MultiDeviceProtocol.Companion.configureIfNeeded(apiDB);
SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, threadDB, this);
setUpP2PAPIIfNeeded();
LokiPushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
if (setUpStorageAPIIfNeeded()) {
if (userPublicKey != null) {
Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey);
LokiFileServerAPI.shared.setDeviceLinks(deviceLinks);
}
}
resubmitProfilePictureIfNeeded();
lokiPublicChatManager = new LokiPublicChatManager(this);
updateOpenGroupProfilePicturesIfNeeded();
registerForFCMIfNeeded(false);
// ========
initializeJobManager(); initializeJobManager();
initializeMessageRetrieval(); initializeMessageRetrieval();
initializeExpiringMessageManager(); initializeExpiringMessageManager();
@ -182,30 +212,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
initializePendingMessages(); initializePendingMessages();
initializeUnidentifiedDeliveryAbilityRefresh(); initializeUnidentifiedDeliveryAbilityRefresh();
initializeBlobProvider(); initializeBlobProvider();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
// Loki - Set up P2P API if needed
setUpP2PAPI();
// Loki - Set up push notification acknowledgement
LokiPushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
// Loki - Update device mappings
if (setUpStorageAPIIfNeeded()) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey != null) {
if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) {
MultiDeviceUtilities.checkIsRevokedSlaveDevice(this);
} else {
// We always update our current device links onto the server in case we failed to do so upon linking
MultiDeviceUtilities.updateDeviceLinksOnServer(this);
}
}
}
// Loki - Resubmit profile picture if needed
resubmitProfilePictureIfNeeded();
// Loki - Set up public chat manager
lokiPublicChatManager = new LokiPublicChatManager(this);
updatePublicChatProfilePictureIfNeeded();
registerForFCMIfNeeded(false);
} }
@Override @Override
@ -214,9 +220,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
Log.i(TAG, "App is now visible."); Log.i(TAG, "App is now visible.");
executePendingContactSync(); executePendingContactSync();
KeyCachingService.onAppForegrounded(this); KeyCachingService.onAppForegrounded(this);
// Loki - Start polling if needed // Loki
startPollingIfNeeded(); startPollingIfNeeded();
// Loki - Start open group polling if needed
lokiPublicChatManager.startPollersIfNeeded(); lokiPublicChatManager.startPollersIfNeeded();
} }
@ -226,14 +231,14 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
Log.i(TAG, "App is no longer visible."); Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this); KeyCachingService.onAppBackgrounded(this);
MessageNotifier.setVisibleThread(-1); MessageNotifier.setVisibleThread(-1);
// Loki - Stop polling if needed // Loki
if (lokiPoller != null) { lokiPoller.stopIfNeeded(); } if (lokiPoller != null) { lokiPoller.stopIfNeeded(); }
if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); } if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); }
} }
@Override @Override
public void onTerminate() { public void onTerminate() {
stopKovenant(); stopKovenant(); // Loki
super.onTerminate(); super.onTerminate();
} }
@ -268,20 +273,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
return persistentLogger; return persistentLogger;
} }
public LokiPublicChatManager getLokiPublicChatManager() { // Loki
return lokiPublicChatManager;
}
public @Nullable LokiPublicChatAPI getLokiPublicChatAPI() { public @Nullable LokiPublicChatAPI getLokiPublicChatAPI() {
if (lokiPublicChatAPI == null && IdentityKeyUtil.hasIdentityKey(this)) { if (lokiPublicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return lokiPublicChatAPI; }
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey != null) { if (userPublicKey== null) { return lokiPublicChatAPI; }
byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
LokiAPIDatabase apiDatabase = DatabaseFactory.getLokiAPIDatabase(this); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(this); LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
lokiPublicChatAPI = new LokiPublicChatAPI(userHexEncodedPublicKey, userPrivateKey, apiDatabase, userDatabase); lokiPublicChatAPI = new LokiPublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB);
}
}
return lokiPublicChatAPI; return lokiPublicChatAPI;
} }
@ -363,8 +363,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
RotateSignedPreKeyListener.schedule(this); RotateSignedPreKeyListener.schedule(this);
LocalBackupListener.schedule(this); LocalBackupListener.schedule(this);
RotateSenderCertificateListener.schedule(this); RotateSenderCertificateListener.schedule(this);
BackgroundPollWorker.schedule(this); // Session BackgroundPollWorker.schedule(this); // Loki
BackgroundOpenGroupPollWorker.schedule(this); // Session BackgroundOpenGroupPollWorker.schedule(this); // Loki
if (BuildConfig.PLAY_STORE_DISABLED) { if (BuildConfig.PLAY_STORE_DISABLED) {
UpdateApkRefreshListener.schedule(this); UpdateApkRefreshListener.schedule(this);
@ -442,18 +442,16 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
// region Loki // region Loki
public boolean setUpStorageAPIIfNeeded() { public boolean setUpStorageAPIIfNeeded() {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey != null && IdentityKeyUtil.hasIdentityKey(this)) { if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { return false; }
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 apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiFileServerAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database); LokiFileServerAPI.Companion.configure(isDebugMode, userPublicKey, userPrivateKey, apiDB);
return true; return true;
}
return false;
} }
public void setUpP2PAPI() { public void setUpP2PAPIIfNeeded() {
String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
if (hexEncodedPublicKey == null) { return; } if (hexEncodedPublicKey == null) { return; }
LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> { LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> {
@ -470,10 +468,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
return; return;
} }
String token = task.getResult().getToken(); String token = task.getResult().getToken();
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context); String userPublicKey = TextSecurePreferences.getLocalNumber(context);
if (userHexEncodedPublicKey == null) return; if (userPublicKey == null) return;
if (TextSecurePreferences.isUsingFCM(this)) { if (TextSecurePreferences.isUsingFCM(this)) {
LokiPushNotificationManager.register(token, userHexEncodedPublicKey, context, force); LokiPushNotificationManager.register(token, userPublicKey, context, force);
} else { } else {
LokiPushNotificationManager.unregister(token, context); LokiPushNotificationManager.unregister(token, context);
} }
@ -487,15 +485,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
private void setUpPollingIfNeeded() { private void setUpPollingIfNeeded() {
if (lokiPoller != null) return; if (lokiPoller != null) return;
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey == null) return; if (userPublicKey == null) return;
LokiAPIDatabase lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(this); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
Context context = this; Context context = this;
LokiSwarmAPI.Companion.configureIfNeeded(lokiAPIDatabase); LokiSwarmAPI.Companion.configureIfNeeded(apiDB);
LokiAPI.Companion.configureIfNeeded(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster); LokiAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
lokiPoller = new LokiPoller(userHexEncodedPublicKey, lokiAPIDatabase, protos -> { lokiPoller = new LokiPoller(userPublicKey, apiDB, protos -> {
for (SignalServiceProtos.Envelope proto : protos) { for (SignalServiceProtos.Envelope proto : protos) {
new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto)); new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto), false);
} }
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
@ -506,94 +504,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (lokiPoller != null) { lokiPoller.startIfNeeded(); } if (lokiPoller != null) { lokiPoller.startIfNeeded(); }
} }
private LokiRSSFeed lokiNewsFeed() {
return new LokiRSSFeed("loki.network.feed", "https://loki.network/feed/", "Loki News", true);
}
private LokiRSSFeed lokiMessengerUpdatesFeed() {
return new LokiRSSFeed("loki.network.messenger-updates.feed", "https://loki.network/category/messenger-updates/feed", "Session Updates", false);
}
public void createDefaultPublicChatsIfNeeded() {
List<LokiPublicChat> defaultPublicChats = LokiPublicChatAPI.Companion.getDefaultChats(BuildConfig.DEBUG);
for (LokiPublicChat publicChat : defaultPublicChats) {
long threadID = GroupManager.getPublicChatThreadId(publicChat.getId(), this);
String migrationKey = publicChat.getId() + "_migrated";
boolean isChatMigrated = TextSecurePreferences.getBooleanPreference(this, migrationKey, false);
boolean isChatSetUp = TextSecurePreferences.isChatSetUp(this, publicChat.getId());
if (!isChatSetUp || !publicChat.isDeletable()) {
lokiPublicChatManager.addChat(publicChat.getServer(), publicChat.getChannel(), publicChat.getDisplayName());
TextSecurePreferences.markChatSetUp(this, publicChat.getId());
TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
} else if (threadID > -1 && !isChatMigrated) {
// Migrate the old public chats
DatabaseFactory.getLokiThreadDatabase(this).setPublicChat(publicChat, threadID);
TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
}
}
}
public void createRSSFeedsIfNeeded() {
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
// feeds.add(lokiNewsFeed());
feeds.add(lokiMessengerUpdatesFeed());
for (LokiRSSFeed feed : feeds) {
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
if (!isFeedSetUp || !feed.isDeletable()) {
GroupManager.createRSSFeedGroup(feed.getId(), this, null, feed.getDisplayName());
TextSecurePreferences.markChatSetUp(this, feed.getId());
}
}
}
private void createRSSFeedPollersIfNeeded() {
// Only create the RSS feed pollers if their threads aren't deleted
LokiRSSFeed lokiNewsFeed = lokiNewsFeed();
long lokiNewsFeedThreadID = GroupManager.getRSSFeedThreadId(lokiNewsFeed.getId(), this);
if (lokiNewsFeedThreadID >= 0 && lokiNewsFeedPoller == null) {
lokiNewsFeedPoller = new LokiRSSFeedPoller(this, lokiNewsFeed);
// Set up deletion listeners if needed
setUpThreadDeletionListeners(lokiNewsFeedThreadID, () -> {
if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.stop();
lokiNewsFeedPoller = null;
});
}
// The user can't delete the Session Updates RSS feed
if (lokiMessengerUpdatesFeedPoller == null) {
lokiMessengerUpdatesFeedPoller = new LokiRSSFeedPoller(this, lokiMessengerUpdatesFeed());
}
}
private void setUpThreadDeletionListeners(long threadID, Runnable onDelete) {
if (threadID < 0) { return; }
ContentObserver observer = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// Stop the poller if thread is deleted
try {
if (!DatabaseFactory.getThreadDatabase(getApplicationContext()).hasThread(threadID)) {
onDelete.run();
getContentResolver().unregisterContentObserver(this);
}
} catch (Exception e) {
// TODO: Handle
}
}
};
this.getContentResolver().registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer);
}
public void startRSSFeedPollersIfNeeded() {
createRSSFeedPollersIfNeeded();
if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.startIfNeeded();
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
}
private void resubmitProfilePictureIfNeeded() { private void resubmitProfilePictureIfNeeded() {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (userHexEncodedPublicKey == null) return; if (userPublicKey == null) return;
long now = new Date().getTime(); long now = new Date().getTime();
long lastProfilePictureUpload = TextSecurePreferences.getLastProfilePictureUpload(this); long lastProfilePictureUpload = TextSecurePreferences.getLastProfilePictureUpload(this);
if (now - lastProfilePictureUpload <= 14 * 24 * 60 * 60 * 1000) return; if (now - lastProfilePictureUpload <= 14 * 24 * 60 * 60 * 1000) return;
@ -601,7 +514,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this); String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this);
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey); byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey);
try { try {
File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userHexEncodedPublicKey)); File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey));
StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length()); StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length());
LokiFileServerAPI.shared.uploadProfilePicture(LokiFileServerAPI.shared.getServer(), profileKey, stream, () -> { LokiFileServerAPI.shared.uploadProfilePicture(LokiFileServerAPI.shared.getServer(), profileKey, stream, () -> {
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime()); TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
@ -615,7 +528,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
}); });
} }
public void updatePublicChatProfilePictureIfNeeded() { public void updateOpenGroupProfilePicturesIfNeeded() {
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
LokiPublicChatAPI publicChatAPI = null; LokiPublicChatAPI publicChatAPI = null;
try { try {
@ -623,48 +536,46 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
} catch (Exception e) { } catch (Exception e) {
// Do nothing // Do nothing
} }
if (publicChatAPI != null) { if (publicChatAPI == null) { return; }
byte[] profileKey = ProfileKeyUtil.getProfileKey(this); byte[] profileKey = ProfileKeyUtil.getProfileKey(this);
String url = TextSecurePreferences.getProfileAvatarUrl(this); String url = TextSecurePreferences.getProfilePictureURL(this);
String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this); String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this);
if (ourMasterDevice != null) { if (userMasterDevice != null) {
Recipient masterDevice = Recipient.from(this, Address.fromSerialized(ourMasterDevice), false).resolve(); Recipient userMasterDeviceAsRecipient = Recipient.from(this, Address.fromSerialized(userMasterDevice), false).resolve();
profileKey = masterDevice.getProfileKey(); profileKey = userMasterDeviceAsRecipient.getProfileKey();
url = masterDevice.getProfileAvatar(); url = userMasterDeviceAsRecipient.getProfileAvatar();
} }
Set<String> servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers(); Set<String> servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers();
for (String server : servers) { for (String server : servers) {
if (profileKey != null) { if (profileKey != null) {
publicChatAPI.setProfilePicture(server, profileKey, url); publicChatAPI.setProfilePicture(server, profileKey, url);
}
} }
} }
}); });
} }
public void checkNeedsDatabaseReset() {
if (TextSecurePreferences.resetDatabase(this)) {
boolean wasUnlinked = TextSecurePreferences.databaseResetFromUnpair(this);
TextSecurePreferences.clearAll(this);
TextSecurePreferences.setDatabaseResetFromUnpair(this, wasUnlinked); // Loki - Re-set the preference so we can use it in the starting screen to determine whether device was unlinked or not
MasterSecretUtil.clear(this);
if (this.deleteDatabase("signal.db")) {
Log.d("Loki", "Deleted database");
}
}
}
public void clearData() { public void clearData() {
TextSecurePreferences.setResetDatabase(this, true); boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this);
new Handler().postDelayed(this::restartApplication, 200); TextSecurePreferences.clearAll(this);
TextSecurePreferences.setWasUnlinked(this, wasUnlinked);
MasterSecretUtil.clear(this);
if (!deleteDatabase("signal.db")) {
Log.d("Loki", "Failed to delete database.");
}
Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200));
} }
public void restartApplication() { public void restartApplication() {
Intent intent = new Intent(this, HomeActivity.class); Intent intent = new Intent(this, HomeActivity.class);
ComponentName componentName = intent.getComponent(); startActivity(Intent.makeRestartActivityTask(intent.getComponent()));
Intent mainIntent = Intent.makeRestartActivityTask(componentName);
this.startActivity(mainIntent);
Runtime.getRuntime().exit(0); Runtime.getRuntime().exit(0);
} }
@Override
public void sendSessionRequest(@NotNull String publicKey) {
DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime());
EphemeralMessage sessionRequest = EphemeralMessage.createSessionRequest(publicKey);
jobManager.add(new PushEphemeralMessageSendJob(sessionRequest));
}
// endregion // endregion
} }

View File

@ -41,7 +41,7 @@ import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment; import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment; import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
@ -345,16 +345,16 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
} }
String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish()); String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish());
new AlertDialog.Builder(getContext()) new AlertDialog.Builder(getContext())
.setTitle(R.string.activity_settings_seed_dialog_title) .setTitle(R.string.activity_settings_seed_dialog_title)
.setMessage(seed) .setMessage(seed)
.setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> { .setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("seed", seed); ClipData clip = ClipData.newPlainText("seed", seed);
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show();
}) })
.setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null) .setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null)
.show(); .show();
} catch (Exception e) { } catch (Exception e) {
Log.d("Loki", e.getMessage()); Log.d("Loki", e.getMessage());
} }

View File

@ -20,11 +20,11 @@ import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import org.thoughtcrime.securesms.components.ContactFilterToolbar; import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode; import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import network.loki.messenger.R; import network.loki.messenger.R;
@ -36,8 +36,8 @@ import network.loki.messenger.R;
* *
*/ */
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
implements SwipeRefreshLayout.OnRefreshListener, implements SwipeRefreshLayout.OnRefreshListener,
ContactSelectionListFragment.OnContactSelectedListener ContactSelectionListFragment.OnContactSelectedListener
{ {
private static final String TAG = ContactSelectionActivity.class.getSimpleName(); private static final String TAG = ContactSelectionActivity.class.getSimpleName();
@ -57,9 +57,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override @Override
protected void onCreate(Bundle icicle, boolean ready) { protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) { if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_ALL);
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS;
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
} }
setContentView(R.layout.contact_selection_activity); setContentView(R.layout.contact_selection_activity);

View File

@ -1,303 +0,0 @@
/*
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import network.loki.messenger.R;
/**
* Fragment for selecting a one or more contacts from a list.
*
* @author Moxie Marlinspike
*
*/
public class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
{
@SuppressWarnings("unused")
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
public static final String DISPLAY_MODE = "display_mode";
public static final String MULTI_SELECT = "multi_select";
public static final String REFRESHABLE = "refreshable";
public static final String RECENTS = "recents";
private TextView emptyText;
private Set<String> selectedContacts;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
initializeCursor();
}
@Override
public void onStart() {
super.onStart();
handleContactPermissionGranted();
// Permissions.with(this)
// .request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
// .ifNecessary()
// .onAllGranted(() -> {
// if (!TextSecurePreferences.hasSuccessfullyRetrievedDirectory(getActivity())) {
// handleContactPermissionGranted();
// } else {
// this.getLoaderManager().initLoader(0, null, this);
// }
// })
// .onAnyDenied(() -> {
// getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
//
// if (getActivity().getIntent().getBooleanExtra(RECENTS, false)) {
// getLoaderManager().initLoader(0, null, ContactSelectionListFragment.this);
// } else {
// initializeNoContactsPermission();
// }
// })
// .execute();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
emptyText = ViewUtil.findById(view, android.R.id.empty);
recyclerView = ViewUtil.findById(view, R.id.recycler_view);
swipeRefresh = ViewUtil.findById(view, R.id.swipe_refresh);
fastScroller = ViewUtil.findById(view, R.id.fast_scroller);
showContactsLayout = view.findViewById(R.id.show_contacts_container);
showContactsButton = view.findViewById(R.id.show_contacts_button);
showContactsDescription = view.findViewById(R.id.show_contacts_description);
showContactsProgress = view.findViewById(R.id.progress);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
swipeRefresh.setEnabled(getActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
return view;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
public @NonNull List<String> getSelectedContacts() {
List<String> selected = new LinkedList<>();
if (selectedContacts != null) {
selected.addAll(selectedContacts);
}
return selected;
}
private boolean isMulti() {
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
}
private void initializeCursor() {
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
GlideApp.with(this),
null,
new ListClickListener(),
isMulti());
selectedContacts = adapter.getSelectedContacts();
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(adapter, true, true));
}
private void initializeNoContactsPermission() {
swipeRefresh.setVisibility(View.GONE);
showContactsLayout.setVisibility(View.VISIBLE);
showContactsProgress.setVisibility(View.INVISIBLE);
showContactsDescription.setText(R.string.contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them);
showContactsButton.setVisibility(View.VISIBLE);
/*
showContactsButton.setOnClickListener(v -> {
Permissions.with(this)
.request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
.ifNecessary()
.withPermanentDenialDialog(getString(R.string.ContactSelectionListFragment_signal_requires_the_contacts_permission_in_order_to_display_your_contacts))
.onSomeGranted(permissions -> {
if (permissions.contains(Manifest.permission.WRITE_CONTACTS)) {
handleContactPermissionGranted();
}
})
.execute();
});
*/
}
public void setQueryFilter(String filter) {
this.cursorFilter = filter;
this.getLoaderManager().restartLoader(0, null, this);
}
public void resetQueryFilter() {
setQueryFilter(null);
swipeRefresh.setRefreshing(false);
}
public void setRefreshing(boolean refreshing) {
swipeRefresh.setRefreshing(refreshing);
}
public void reset() {
selectedContacts.clear();
if (!isDetached() && !isRemoving() && getActivity() != null && !getActivity().isFinishing()) {
getLoaderManager().restartLoader(0, null, this);
}
}
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new ContactsCursorLoader(getActivity(),
getActivity().getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false));
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
swipeRefresh.setVisibility(View.VISIBLE);
showContactsLayout.setVisibility(View.GONE);
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(data);
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
boolean useFastScroller = (recyclerView.getAdapter().getItemCount() > 20);
recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
if (useFastScroller) {
fastScroller.setVisibility(View.VISIBLE);
fastScroller.setRecyclerView(recyclerView);
}
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(null);
fastScroller.setVisibility(View.GONE);
}
@SuppressLint("StaticFieldLeak")
private void handleContactPermissionGranted() {
new AsyncTask<Void, Void, Boolean>() {
@Override
protected void onPreExecute() {
swipeRefresh.setVisibility(View.GONE);
showContactsLayout.setVisibility(View.VISIBLE);
showContactsButton.setVisibility(View.INVISIBLE);
showContactsDescription.setText(R.string.ConversationListFragment_loading);
showContactsProgress.setVisibility(View.VISIBLE);
showContactsProgress.spin();
}
@Override
protected Boolean doInBackground(Void... voids) {
return false;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
showContactsLayout.setVisibility(View.GONE);
swipeRefresh.setVisibility(View.VISIBLE);
reset();
} else {
Toast.makeText(getContext(), R.string.ContactSelectionListFragment_error_retrieving_contacts_check_your_network_connection, Toast.LENGTH_LONG).show();
initializeNoContactsPermission();
}
}
}.execute();
}
private class ListClickListener implements ContactSelectionListAdapter.ItemClickListener {
@Override
public void onItemClick(ContactSelectionListItem contact) {
if (!isMulti() || !selectedContacts.contains(contact.getNumber())) {
selectedContacts.add(contact.getNumber());
contact.setChecked(true);
if (onContactSelectedListener != null) onContactSelectedListener.onContactSelected(contact.getNumber());
} else {
selectedContacts.remove(contact.getNumber());
contact.setChecked(false);
if (onContactSelectedListener != null) onContactSelectedListener.onContactDeselected(contact.getNumber());
}
}
}
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
this.onContactSelectedListener = onContactSelectedListener;
}
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
}
public interface OnContactSelectedListener {
void onContactSelected(String number);
void onContactDeselected(String number);
}
}

View File

@ -49,8 +49,8 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog; import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
import org.thoughtcrime.securesms.loki.RecipientAvatarModifiedEvent; import org.thoughtcrime.securesms.loki.utilities.ProfilePictureModifiedEvent;
import org.thoughtcrime.securesms.loki.redesign.activities.JoinPublicChatActivity; import org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
@ -86,13 +86,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
protected void onPreCreate() { protected void onPreCreate() {
dynamicTheme.onCreate(this); dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this); dynamicLanguage.onCreate(this);
if (TextSecurePreferences.getLocalNumber(this) != null) {
ApplicationContext application = ApplicationContext.getInstance(this);
application.createDefaultPublicChatsIfNeeded();
application.createRSSFeedsIfNeeded();
application.getLokiPublicChatManager().startPollersIfNeeded();
application.startRSSFeedPollersIfNeeded();
}
} }
@Override @Override
@ -330,9 +323,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
public void onAvatarModified(RecipientAvatarModifiedEvent event) { public void onAvatarModified(ProfilePictureModifiedEvent event) {
Recipient recipient = event.getRecipient(); Recipient recipient = event.getRecipient();
if (recipient.isLocalNumber() || recipient.isOurMasterDevice()) { if (recipient.isLocalNumber() || recipient.isUserMasterDevice()) {
initializeProfileIcon(recipient); initializeProfileIcon(recipient);
} }
} }

View File

@ -74,7 +74,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader; import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent; import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
import org.thoughtcrime.securesms.loki.redesign.activities.CreatePrivateChatActivity; import org.thoughtcrime.securesms.loki.activities.CreatePrivateChatActivity;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;

View File

@ -37,8 +37,8 @@ import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.TypingIndicatorView; import org.thoughtcrime.securesms.components.TypingIndicatorView;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
import org.thoughtcrime.securesms.loki.redesign.utilities.MentionUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
@ -272,7 +272,7 @@ public class ConversationListItem extends RelativeLayout
} }
private @NonNull CharSequence getTrimmedSnippet(@NonNull CharSequence snippet) { private @NonNull CharSequence getTrimmedSnippet(@NonNull CharSequence snippet) {
LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, getContext()); // TODO: Terrible place to do this, but okay for now MentionManagerUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, getContext()); // TODO: Terrible place to do this, but okay for now
snippet = MentionUtilities.highlightMentions(snippet, threadId, getContext()); snippet = MentionUtilities.highlightMentions(snippet, threadId, getContext());
return snippet.length() <= MAX_SNIPPET_LENGTH ? snippet : snippet.subSequence(0, MAX_SNIPPET_LENGTH); return snippet.length() <= MAX_SNIPPET_LENGTH ? snippet : snippet.subSequence(0, MAX_SNIPPET_LENGTH);
} }

View File

@ -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.fileserver.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -406,7 +406,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
String newProfileKey = ProfileKeyUtil.generateEncodedProfileKey(context); String newProfileKey = ProfileKeyUtil.generateEncodedProfileKey(context);
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(newProfileKey); byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(newProfileKey);
//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");
LokiFileServerAPI storageAPI = LokiFileServerAPI.shared; LokiFileServerAPI storageAPI = LokiFileServerAPI.shared;
@ -415,9 +415,9 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
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.setProfilePictureURL(context, result.getUrl());
} else { } else {
TextSecurePreferences.setProfileAvatarUrl(context, null); TextSecurePreferences.setProfilePictureURL(context, null);
} }
AvatarHelper.setAvatar(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes); AvatarHelper.setAvatar(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes);
@ -427,7 +427,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).updatePublicChatProfilePictureIfNeeded(); ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded();
} 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;

View File

@ -18,7 +18,7 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription; import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.service.ApplicationMigrationService; import org.thoughtcrime.securesms.service.ApplicationMigrationService;
import org.thoughtcrime.securesms.service.ApplicationMigrationService.ImportState; import org.thoughtcrime.securesms.service.ApplicationMigrationService.ImportState;

View File

@ -25,8 +25,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.loaders.DeviceListLoader; import org.thoughtcrime.securesms.database.loaders.DeviceListLoader;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.devicelist.Device; import org.thoughtcrime.securesms.devicelist.Device;
import org.thoughtcrime.securesms.loki.redesign.dialogs.DeviceEditingOptionsBottomSheet; import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet;
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Function; import org.whispersystems.libsignal.util.guava.Function;
@ -39,7 +39,7 @@ import kotlin.Pair;
import kotlin.Unit; import kotlin.Unit;
import network.loki.messenger.R; import network.loki.messenger.R;
import static org.thoughtcrime.securesms.loki.GeneralUtilitiesKt.toPx; import static org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt.toPx;
public class DeviceListFragment extends ListFragment public class DeviceListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<List<Device>>, implements LoaderManager.LoaderCallbacks<List<Device>>,

View File

@ -19,7 +19,7 @@ import com.nineoldandroids.animation.ArgbEvaluator;
import org.thoughtcrime.securesms.IntroPagerAdapter.IntroPage; import org.thoughtcrime.securesms.IntroPagerAdapter.IntroPage;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;

View File

@ -43,7 +43,6 @@ import com.bumptech.glide.request.transition.Transition;
import org.thoughtcrime.securesms.avatar.AvatarSelection; import org.thoughtcrime.securesms.avatar.AvatarSelection;
import org.thoughtcrime.securesms.components.PushRecipientsPanel; import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener; import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.RecipientsEditor; import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
@ -57,6 +56,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult; import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
@ -321,11 +322,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class); Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
if (groupToUpdate.isPresent()) { intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH);
} else {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH | DisplayMode.FLAG_SMS);
}
startActivityForResult(intent, PICK_CONTACT); startActivityForResult(intent, PICK_CONTACT);
} }
} }

View File

@ -26,9 +26,10 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.ContactFilterToolbar; import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener; import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@ -40,7 +41,7 @@ import java.util.concurrent.ExecutionException;
import network.loki.messenger.R; import network.loki.messenger.R;
public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener { public class InviteActivity extends PassphraseRequiredActionBarActivity {
private ContactSelectionListFragment contactsFragment; private ContactSelectionListFragment contactsFragment;
private EditText inviteText; private EditText inviteText;
@ -52,7 +53,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
@Override @Override
protected void onCreate(Bundle savedInstanceState, boolean ready) { protected void onCreate(Bundle savedInstanceState, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_SMS); getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true); getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false); getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
@ -84,7 +85,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
heart.getViewTreeObserver().addOnPreDrawListener(new HeartPreDrawListener()); heart.getViewTreeObserver().addOnPreDrawListener(new HeartPreDrawListener());
} }
contactsFragment.setOnContactSelectedListener(this);
shareButton.setOnClickListener(new ShareClickListener()); shareButton.setOnClickListener(new ShareClickListener());
smsButton.setOnClickListener(new SmsClickListener()); smsButton.setOnClickListener(new SmsClickListener());
smsCancelButton.setOnClickListener(new SmsCancelClickListener()); smsCancelButton.setOnClickListener(new SmsCancelClickListener());
@ -99,12 +99,10 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
return animation; return animation;
} }
@Override
public void onContactSelected(String number) { public void onContactSelected(String number) {
updateSmsButtonText(); updateSmsButtonText();
} }
@Override
public void onContactDeselected(String number) { public void onContactDeselected(String number) {
updateSmsButtonText(); updateSmsButtonText();
} }
@ -132,7 +130,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
} }
private void cancelSmsSelection() { private void cancelSmsSelection() {
contactsFragment.reset();
updateSmsButtonText(); updateSmsButtonText();
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE); ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE);
} }
@ -241,7 +238,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE).addListener(new Listener<Boolean>() { ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE).addListener(new Listener<Boolean>() {
@Override @Override
public void onSuccess(Boolean result) { public void onSuccess(Boolean result) {
contactsFragment.reset();
} }
@Override @Override

View File

@ -47,7 +47,7 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader; import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;

View File

@ -13,9 +13,9 @@ import android.support.v4.app.Fragment;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.redesign.activities.LandingActivity; import org.thoughtcrime.securesms.loki.activities.LandingActivity;
import org.thoughtcrime.securesms.loki.redesign.activities.SeedActivity; import org.thoughtcrime.securesms.loki.activities.SeedActivity;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;

View File

@ -19,6 +19,8 @@ package org.thoughtcrime.securesms;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -618,7 +618,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
unidentifiedAccessKey, universalUnidentifiedAccess); unidentifiedAccessKey, universalUnidentifiedAccess);
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this); IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this);
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(RegistrationActivity.this); List<PreKeyRecord> records = PreKeyUtil.generatePreKeyRecords(RegistrationActivity.this);
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(RegistrationActivity.this, identityKey, true); SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(RegistrationActivity.this, identityKey, true);
accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records); accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records);

View File

@ -29,19 +29,19 @@ import android.os.Process;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import org.thoughtcrime.securesms.components.SearchToolbar; import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.conversation.ConversationActivity; import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.Address; 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.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.providers.BlobProvider;
@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FileUtils; import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -68,7 +67,7 @@ import network.loki.messenger.R;
* @author Jake McGinty * @author Jake McGinty
*/ */
public class ShareActivity extends PassphraseRequiredActionBarActivity public class ShareActivity extends PassphraseRequiredActionBarActivity
implements ContactSelectionListFragment.OnContactSelectedListener, SwipeRefreshLayout.OnRefreshListener implements ContactSelectionListFragment.OnContactSelectedListener
{ {
private static final String TAG = ShareActivity.class.getSimpleName(); private static final String TAG = ShareActivity.class.getSimpleName();
@ -96,14 +95,10 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected void onCreate(Bundle icicle, boolean ready) { protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) { if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_ALL);
TextSecurePreferences.isSmsEnabled(this)
? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS);
} }
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false); getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
getIntent().putExtra(ContactSelectionListFragment.RECENTS, true);
setContentView(R.layout.share_activity); setContentView(R.layout.share_activity);
@ -170,7 +165,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
searchAction = findViewById(R.id.search_action); searchAction = findViewById(R.id.search_action);
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment); contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(this); contactsFragment.setOnContactSelectedListener(this);
contactsFragment.setOnRefreshListener(this);
} }
private void initializeSearch() { private void initializeSearch() {
@ -281,12 +275,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onContactDeselected(String number) { public void onContactDeselected(String number) {
}
@Override
public void onRefresh() {
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")

View File

@ -11,7 +11,7 @@ import android.support.v7.app.AppCompatActivity;
import android.widget.Toast; import android.widget.Toast;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.CommunicationActions; import org.thoughtcrime.securesms.util.CommunicationActions;

View File

@ -80,7 +80,7 @@ public class AvatarImageView extends AppCompatImageView {
setOutlineProvider(new ViewOutlineProvider() { setOutlineProvider(new ViewOutlineProvider() {
@Override @Override
public void getOutline(View view, Outline outline) { public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight()); outline.setOval(0, 0, view.getWidth(), view.getHeight());
} }
}); });
setClipToOutline(true); setClipToOutline(true);
@ -132,11 +132,11 @@ public class AvatarImageView extends AppCompatImageView {
if (photo.contactPhoto != null) { if (photo.contactPhoto != null) {
requestManager.load(photo.contactPhoto) requestManager.load(photo.contactPhoto)
.fallback(fallbackContactPhotoDrawable) .fallback(fallbackContactPhotoDrawable)
.error(fallbackContactPhotoDrawable) .error(fallbackContactPhotoDrawable)
.diskCacheStrategy(DiskCacheStrategy.ALL) .diskCacheStrategy(DiskCacheStrategy.ALL)
.circleCrop() .circleCrop()
.into(this); .into(this);
} else { } else {
setImageDrawable(fallbackContactPhotoDrawable); setImageDrawable(fallbackContactPhotoDrawable);
} }
@ -184,9 +184,9 @@ public class AvatarImageView extends AppCompatImageView {
if (other == null) return false; if (other == null) return false;
return other.recipient.equals(recipient) && return other.recipient.equals(recipient) &&
other.recipient.getColor().equals(recipient.getColor()) && other.recipient.getColor().equals(recipient.getColor()) &&
other.ready == ready && other.ready == ready &&
Objects.equals(other.contactPhoto, contactPhoto); Objects.equals(other.contactPhoto, contactPhoto);
} }
} }
} }

View File

@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdap
import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.utilities.MentionUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.QuoteModel; import org.thoughtcrime.securesms.mms.QuoteModel;

View File

@ -75,7 +75,6 @@ public class LinkPreviewView extends FrameLayout {
container.setBackgroundColor(Color.TRANSPARENT); container.setBackgroundColor(Color.TRANSPARENT);
container.setPadding(0, 0, 0, 0); container.setPadding(0, 0, 0, 0);
divider.setVisibility(VISIBLE); divider.setVisibility(VISIBLE);
// closeButton.setVisibility(VISIBLE);
closeButton.setOnClickListener(v -> { closeButton.setOnClickListener(v -> {
if (closeClickedListener != null) { if (closeClickedListener != null) {

View File

@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import java.util.List; import java.util.List;

View File

@ -108,11 +108,7 @@ public class SendButton extends AppCompatImageButton
@Override @Override
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
// if (isEnabled() && transportOptions.getEnabledTransports().size() > 1) { // Loki - Do nothing
// getTransportOptionsPopup().display(transportOptions.getEnabledTransports());
// return true;
// }
return false; return false;
} }
} }

View File

@ -200,7 +200,6 @@ public class TransferControlView extends FrameLayout {
if (view != null) { if (view != null) {
view.setVisibility(VISIBLE); view.setVisibility(VISIBLE);
// setVisibility(VISIBLE);
} else { } else {
setVisibility(GONE); setVisibility(GONE);
} }

View File

@ -9,17 +9,16 @@ 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.protocol.SessionMetaProtocol;
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.multidevice.LokiDeviceLinkUtilities; import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.api.fileserver.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;
@SuppressLint("UseSparseArrays") @SuppressLint("UseSparseArrays")
public class TypingStatusSender { public class TypingStatusSender {
@ -82,24 +81,17 @@ public class TypingStatusSender {
} }
private void sendTyping(long threadId, boolean typingStarted) { private void sendTyping(long threadId, boolean typingStarted) {
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);
// Loki - Check whether we want to send a typing indicator to this user
if (recipient == null) { if (!SessionMetaProtocol.shouldSendTypingIndicator(recipient, context)) { return; }
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted)); // Loki - Take into account multi device
return; Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize());
for (String device : linkedDevices) {
Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false);
long deviceThreadID = threadDatabase.getThreadIdFor(deviceAsRecipient);
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted));
} }
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(recipient.getAddress().serialize()).success(devices -> {
for (String device : devices) {
Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false);
long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient);
if (deviceThreadID > -1) {
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted));
}
}
return Unit.INSTANCE;
});
} }
private class StartRunnable implements Runnable { private class StartRunnable implements Runnable {

View File

@ -8,9 +8,9 @@ import android.provider.Telephony;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import network.loki.messenger.R;
public class DefaultSmsReminder extends Reminder { public class DefaultSmsReminder extends Reminder {
@ -40,14 +40,5 @@ public class DefaultSmsReminder extends Reminder {
public static boolean isEligible(Context context) { public static boolean isEligible(Context context) {
return false; return false;
// Loki - Original code
// ========
// final boolean isDefault = Util.isDefaultSmsProvider(context);
// if (isDefault) {
// TextSecurePreferences.setPromptedDefaultSmsProvider(context, false);
// }
//
// return !isDefault && !TextSecurePreferences.hasPromptedDefaultSmsProvider(context);
// ========
} }
} }

View File

@ -4,9 +4,9 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.RegistrationActivity; import org.thoughtcrime.securesms.RegistrationActivity;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import network.loki.messenger.R;
public class PushRegistrationReminder extends Reminder { public class PushRegistrationReminder extends Reminder {
@ -30,9 +30,5 @@ public class PushRegistrationReminder extends Reminder {
public static boolean isEligible(Context context) { public static boolean isEligible(Context context) {
return false; return false;
// Loki - Original code
// ========
// return !TextSecurePreferences.isPushRegistered(context);
// ========
} }
} }

View File

@ -1,271 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller.FastScrollAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* List adapter to display all contacts and their related information
*
* @author Jake McGinty
*/
public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewHolder>
implements FastScrollAdapter,
StickyHeaderAdapter<HeaderViewHolder>
{
private final static String TAG = ContactSelectionListAdapter.class.getSimpleName();
private static final int VIEW_TYPE_CONTACT = 0;
private static final int VIEW_TYPE_DIVIDER = 1;
private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
R.attr.contact_selection_lay_user};
private final boolean multiSelect;
private final LayoutInflater li;
private final TypedArray drawables;
private final ItemClickListener clickListener;
private final GlideRequests glideRequests;
private final Set<String> selectedContacts = new HashSet<>();
public abstract static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect);
public abstract void unbind(@NonNull GlideRequests glideRequests);
public abstract void setChecked(boolean checked);
}
public static class ContactViewHolder extends ViewHolder {
ContactViewHolder(@NonNull final View itemView,
@Nullable final ItemClickListener clickListener)
{
super(itemView);
itemView.setOnClickListener(v -> {
if (clickListener != null) clickListener.onItemClick(getView());
});
}
public ContactSelectionListItem getView() {
return (ContactSelectionListItem) itemView;
}
public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
getView().set(glideRequests, type, name, number, label, color, multiSelect);
}
@Override
public void unbind(@NonNull GlideRequests glideRequests) {
getView().unbind(glideRequests);
}
@Override
public void setChecked(boolean checked) {
getView().setChecked(checked);
}
}
public static class DividerViewHolder extends ViewHolder {
private final TextView label;
DividerViewHolder(View itemView) {
super(itemView);
this.label = itemView.findViewById(R.id.label);
}
@Override
public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
this.label.setText(name);
}
@Override
public void unbind(@NonNull GlideRequests glideRequests) {}
@Override
public void setChecked(boolean checked) {}
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
HeaderViewHolder(View itemView) {
super(itemView);
}
}
public ContactSelectionListAdapter(@NonNull Context context,
@NonNull GlideRequests glideRequests,
@Nullable Cursor cursor,
@Nullable ItemClickListener clickListener,
boolean multiSelect)
{
super(context, cursor);
this.li = LayoutInflater.from(context);
this.glideRequests = glideRequests;
this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
this.multiSelect = multiSelect;
this.clickListener = clickListener;
}
@Override
public long getHeaderId(int i) {
if (!isActiveCursor()) return -1;
int contactType = getContactType(i);
if (contactType == ContactsDatabase.DIVIDER_TYPE) return -1;
return Util.hashCode(getHeaderString(i), getContactType(i));
}
@Override
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_CONTACT) {
return new ContactViewHolder(li.inflate(R.layout.contact_selection_list_item, parent, false), clickListener);
} else {
return new DividerViewHolder(li.inflate(R.layout.contact_selection_list_divider, parent, false));
}
}
@Override
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN));
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN));
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getContext().getResources(),
numberType, label).toString();
int color = (contactType == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
drawables.getColor(1, 0xff000000);
viewHolder.unbind(glideRequests);
viewHolder.bind(glideRequests, contactType, name, number, labelText, color, multiSelect);
viewHolder.setChecked(selectedContacts.contains(number));
}
@Override
public int getItemViewType(@NonNull Cursor cursor) {
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN)) == ContactsDatabase.DIVIDER_TYPE) {
return VIEW_TYPE_DIVIDER;
} else {
return VIEW_TYPE_CONTACT;
}
}
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.contact_selection_recyclerview_header, parent, false));
}
@Override
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
((TextView)viewHolder.itemView).setText(getSpannedHeaderString(position));
}
@Override
public void onItemViewRecycled(ViewHolder holder) {
holder.unbind(glideRequests);
}
@Override
public CharSequence getBubbleText(int position) {
return getHeaderString(position);
}
public Set<String> getSelectedContacts() {
return selectedContacts;
}
private CharSequence getSpannedHeaderString(int position) {
final String headerString = getHeaderString(position);
if (isPush(position)) {
SpannableString spannable = new SpannableString(headerString);
spannable.setSpan(new ForegroundColorSpan(getContext().getResources().getColor(R.color.signal_primary)), 0, headerString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannable;
} else {
return headerString;
}
}
private @NonNull String getHeaderString(int position) {
int contactType = getContactType(position);
if (contactType == ContactsDatabase.RECENT_TYPE || contactType == ContactsDatabase.DIVIDER_TYPE) {
return " ";
}
Cursor cursor = getCursorAtPositionOrThrow(position);
String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
if (!TextUtils.isEmpty(letter)) {
String firstChar = letter.trim().substring(0, 1).toUpperCase();
if (Character.isLetterOrDigit(firstChar.codePointAt(0))) {
return firstChar;
}
}
return "#";
}
private int getContactType(int position) {
final Cursor cursor = getCursorAtPositionOrThrow(position);
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
}
private boolean isPush(int position) {
return getContactType(position) == ContactsDatabase.PUSH_TYPE;
}
public interface ItemClickListener {
void onItemClick(ContactSelectionListItem item);
}
}

View File

@ -1,161 +0,0 @@
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.signalservice.loki.api.LokiAPI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import network.loki.messenger.R;
public class ContactSelectionListItem extends LinearLayout implements RecipientModifiedListener {
@SuppressWarnings("unused")
private static final String TAG = ContactSelectionListItem.class.getSimpleName();
private ProfilePictureView profilePictureView;
private TextView numberView;
private TextView nameView;
private TextView labelView;
private CheckBox checkBox;
private String number;
private Recipient recipient;
private GlideRequests glideRequests;
private long threadID;
public ContactSelectionListItem(Context context) {
super(context);
}
public ContactSelectionListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
this.profilePictureView = findViewById(R.id.profilePictureView);
this.numberView = findViewById(R.id.number);
this.labelView = findViewById(R.id.label);
this.nameView = findViewById(R.id.name);
this.checkBox = findViewById(R.id.check_box);
ViewUtil.setTextViewGravityStart(this.nameView, getContext());
}
public void set(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
this.glideRequests = glideRequests;
this.number = number;
if (type == ContactsDatabase.NEW_TYPE) {
this.recipient = null;
} else if (!TextUtils.isEmpty(number)) {
Address address = Address.fromExternal(getContext(), number);
this.recipient = Recipient.from(getContext(), address, true);
this.recipient.addListener(this);
if (this.recipient.getName() != null) {
name = this.recipient.getName();
}
}
threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
this.numberView.setTextColor(color);
updateProfilePicture(glideRequests, name, threadID);
if (!multiSelect && recipient != null && recipient.isLocalNumber()) {
name = getContext().getString(R.string.note_to_self);
}
setText(type, name, number, label);
if (multiSelect) this.checkBox.setVisibility(View.VISIBLE);
else this.checkBox.setVisibility(View.GONE);
}
public void setChecked(boolean selected) {
this.checkBox.setChecked(selected);
}
public void unbind(GlideRequests glideRequests) {
if (recipient != null) {
recipient.removeListener(this);
recipient = null;
}
}
private void setText(int type, String name, String number, String label) {
if (number == null || number.isEmpty() || GroupUtil.isEncodedGroup(number)) {
this.nameView.setEnabled(false);
this.numberView.setText("");
this.labelView.setVisibility(View.GONE);
} else if (type == ContactsDatabase.PUSH_TYPE) {
this.numberView.setText(number);
this.nameView.setEnabled(true);
this.labelView.setVisibility(View.GONE);
} else {
this.numberView.setText(number);
this.nameView.setEnabled(true);
this.labelView.setText(label);
this.labelView.setVisibility(View.VISIBLE);
}
this.nameView.setText(name);
}
public String getNumber() {
return number;
}
@Override
public void onModified(final Recipient recipient) {
if (this.recipient == recipient) {
Util.runOnMain(() -> {
threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
updateProfilePicture(glideRequests, recipient.getName(), threadID);
nameView.setText(recipient.toShortString());
});
}
}
private void updateProfilePicture(GlideRequests glide, String name, long threadID) {
if (this.recipient.isGroupRecipient()) {
Set<String> usersAsSet = LokiAPI.Companion.getUserHexEncodedPublicKeyCache().get(threadID);
if (usersAsSet == null) {
usersAsSet = new HashSet<>();
}
ArrayList<String> users = new ArrayList<>(usersAsSet);
Collections.sort(users); // Sort to provide a level of stability
profilePictureView.setHexEncodedPublicKey(users.size() > 0 ? users.get(0) : "");
profilePictureView.setAdditionalHexEncodedPublicKey(users.size() > 1 ? users.get(1) : "");
profilePictureView.setRSSFeed(name.equals("Loki News") || name.equals("Session Updates"));
} else {
profilePictureView.setHexEncodedPublicKey(this.number);
profilePictureView.setAdditionalHexEncodedPublicKey(null);
profilePictureView.setRSSFeed(false);
}
profilePictureView.glide = glide;
profilePictureView.update();
}
}

View File

@ -139,7 +139,6 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList; import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -155,16 +154,17 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.LokiThreadDatabaseDelegate; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities; import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
import org.thoughtcrime.securesms.loki.redesign.views.MentionCandidateSelectionView; import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.loki.redesign.views.SessionRestoreBannerView; import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity; import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
import org.thoughtcrime.securesms.mms.AttachmentManager; import org.thoughtcrime.securesms.mms.AttachmentManager;
@ -214,7 +214,6 @@ import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
@ -228,13 +227,12 @@ 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.multidevice.DeviceLink; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat; import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.messaging.Mention;
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
import java.io.IOException; import java.io.IOException;
@ -242,7 +240,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -326,7 +323,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 subtitleTextView; private TextView subtitleTextView;
private AttachmentTypeSelector attachmentTypeSelector; private AttachmentTypeSelector attachmentTypeSelector;
private AttachmentManager attachmentManager; private AttachmentManager attachmentManager;
@ -357,7 +354,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 // Message status bar
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>(); private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
private String messageStatus = null; private String messageStatus = null;
@ -368,9 +365,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ArrayList<Mention> mentions = new ArrayList<>(); private ArrayList<Mention> mentions = new ArrayList<>();
private String oldText = ""; private String oldText = "";
// Multi Device
private boolean isFriendsWithAnyDevice = false;
// Restoration // Restoration
protected SessionRestoreBannerView sessionRestoreBannerView; protected SessionRestoreBannerView sessionRestoreBannerView;
@ -458,7 +452,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}); });
sessionRestoreBannerView.setOnRestore(() -> { sessionRestoreBannerView.setOnRestore(() -> {
this.restoreSession(); SessionManagementProtocol.startSessionReset(this, recipient, threadId);
updateSessionRestoreBanner();
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
sessionRestoreBannerView.setOnDismiss(() -> { sessionRestoreBannerView.setOnDismiss(() -> {
@ -468,7 +463,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, this); MentionManagerUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, this);
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
if (publicChat != null) { if (publicChat != null) {
@ -559,6 +554,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
markThreadAsRead(); markThreadAsRead();
DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this); DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this);
updateInputPanel(); updateInputPanel();
updateSessionRestoreBanner(); updateSessionRestoreBanner();
@ -665,9 +661,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
*/ */
case PICK_GIF: case PICK_GIF:
setMedia(data.getData(), setMedia(data.getData(),
MediaType.GIF, MediaType.GIF,
data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0));
break; break;
case SMS_DEFAULT: case SMS_DEFAULT:
initializeSecurity(isSecureText, isDefaultSms); initializeSecurity(isSecureText, isDefaultSms);
@ -747,7 +743,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MenuInflater inflater = this.getMenuInflater(); MenuInflater inflater = this.getMenuInflater();
menu.clear(); menu.clear();
boolean isOpenGroupOrRSSFeed = recipient.getAddress().isPublicChat() || recipient.getAddress().isRSSFeed(); boolean isOpenGroupOrRSSFeed = recipient.getAddress().isOpenGroup() || recipient.getAddress().isRSSFeed();
if (isSecureText && !isOpenGroupOrRSSFeed) { if (isSecureText && !isOpenGroupOrRSSFeed) {
if (recipient.getExpireMessages() > 0) { if (recipient.getExpireMessages() > 0) {
@ -887,7 +883,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case R.id.menu_invite: handleInviteLink(); return true; case R.id.menu_invite: handleInviteLink(); return true;
case R.id.menu_mute_notifications: handleMuteNotifications(); return true; case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true; case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
// case R.id.menu_conversation_settings: handleConversationSettings(); return true; // case R.id.menu_conversation_settings: handleConversationSettings(); return true;
case R.id.menu_expiring_messages_off: case R.id.menu_expiring_messages_off:
case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
case android.R.id.home: handleReturnToConversationList(); return true; case android.R.id.home: handleReturnToConversationList(); return true;
@ -1154,7 +1150,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)); builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group));
builder.setPositiveButton(R.string.yes, (dialog, which) -> { builder.setPositiveButton(R.string.yes, (dialog, which) -> {
Recipient groupRecipient = getRecipient(); Recipient groupRecipient = getRecipient();
if (GroupUtil.leaveGroup(this, groupRecipient)) { if (ClosedGroupsProtocol.leaveGroup(this, groupRecipient)) {
initializeEnabledCheck(); initializeEnabledCheck();
} else { } else {
Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
@ -1240,11 +1236,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private boolean handleDisplayQuickContact() { private boolean handleDisplayQuickContact() {
return !recipient.getAddress().isGroup(); return !recipient.getAddress().isGroup();
// if (recipient.getContactUri() != null) { /*
// ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null); if (recipient.getContactUri() != null) {
// } else { ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
// handleAddToContacts(); } else {
// } handleAddToContacts();
}
*/
} }
private void handleAddAttachment() { private void handleAddAttachment() {
@ -1531,7 +1529,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
} }
protected void updateSessionRestoreBanner() { private void updateSessionRestoreBanner() {
Set<String> devices = DatabaseFactory.getLokiThreadDatabase(this).getSessionRestoreDevices(threadId); Set<String> devices = DatabaseFactory.getLokiThreadDatabase(this).getSessionRestoreDevices(threadId);
if (devices.size() > 0) { if (devices.size() > 0) {
sessionRestoreBannerView.update(recipient); sessionRestoreBannerView.update(recipient);
@ -1617,30 +1615,30 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void initializeViews() { private void initializeViews() {
titleTextView = findViewById(R.id.titleTextView); titleTextView = findViewById(R.id.titleTextView);
buttonToggle = ViewUtil.findById(this, R.id.button_toggle); buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
sendButton = ViewUtil.findById(this, R.id.send_button); sendButton = ViewUtil.findById(this, R.id.send_button);
attachButton = ViewUtil.findById(this, R.id.attach_button); attachButton = ViewUtil.findById(this, R.id.attach_button);
composeText = ViewUtil.findById(this, R.id.embedded_text_editor); composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
charactersLeft = ViewUtil.findById(this, R.id.space_left); charactersLeft = ViewUtil.findById(this, R.id.space_left);
emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub); emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
unblockButton = ViewUtil.findById(this, R.id.unblock_button); unblockButton = ViewUtil.findById(this, R.id.unblock_button);
makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button); makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
registerButton = ViewUtil.findById(this, R.id.register_button); registerButton = ViewUtil.findById(this, R.id.register_button);
container = ViewUtil.findById(this, R.id.layout_container); container = ViewUtil.findById(this, R.id.layout_container);
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub); reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub); unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub); groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub);
quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle); quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container); inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
inputPanel = ViewUtil.findById(this, R.id.bottom_panel); inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
searchNav = ViewUtil.findById(this, R.id.conversation_search_nav); searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer); mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer);
mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView); mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
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);
subtitleTextView = 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);
@ -1897,15 +1895,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case AttachmentTypeSelector.TAKE_PHOTO: case AttachmentTypeSelector.TAKE_PHOTO:
attachmentManager.capturePhoto(this, TAKE_PHOTO); break; attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
case AttachmentTypeSelector.ADD_GIF: case AttachmentTypeSelector.ADD_GIF:
AlertDialog.Builder builder = new AlertDialog.Builder(this); boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this);
builder.setTitle("Search GIFs?"); if (!hasSeenGIFMetaDataWarning) {
builder.setMessage("You will not have full metadata protection when sending GIFs."); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setPositiveButton("OK", (dialog, which) -> { builder.setTitle("Search GIFs?");
builder.setMessage("You will not have full metadata protection when sending GIFs.");
builder.setPositiveButton("OK", (dialog, which) -> {
AttachmentManager.selectGif(this, PICK_GIF, !isSecureText);
dialog.dismiss();
});
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss());
builder.create().show();
TextSecurePreferences.setHasSeenGIFMetaDataWarning(this);
} else {
AttachmentManager.selectGif(this, PICK_GIF, !isSecureText); AttachmentManager.selectGif(this, PICK_GIF, !isSecureText);
dialog.dismiss(); }
});
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss() );
builder.create().show();
break; break;
} }
} }
@ -2073,7 +2077,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void setGroupShareProfileReminder(@NonNull Recipient recipient) { private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing() && !recipient.getAddress().isPublicChat() && !recipient.getAddress().isRSSFeed()) { if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing() && !recipient.getAddress().isOpenGroup() && !recipient.getAddress().isRSSFeed()) {
groupShareProfileView.get().setRecipient(recipient); groupShareProfileView.get().setRecipient(recipient);
groupShareProfileView.get().setVisibility(View.GONE); // Loki - Always hide for now groupShareProfileView.get().setVisibility(View.GONE); // Loki - Always hide for now
} else if (groupShareProfileView.resolved()) { } else if (groupShareProfileView.resolved()) {
@ -2249,72 +2253,39 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void handleThreadFriendRequestStatusChanged(long threadID) { public void handleThreadFriendRequestStatusChanged(long threadID) {
if (threadID != this.threadId) { if (recipient.isGroupRecipient()) { return; }
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID); boolean isUpdateNeeded = false;
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) { if (threadID == this.threadId) {
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(threadRecipient.getAddress().serialize()).success(devices -> { isUpdateNeeded = true;
// We should update our input if this thread is a part of the other threads device } else {
if (devices.contains(recipient.getAddress().serialize())) { String thisThreadPublicKey = recipient.getAddress().serialize();
this.updateInputPanel(); Set<String> thisThreadAssociatedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(thisThreadPublicKey);
} Recipient changedThreadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
return Unit.INSTANCE; String changedThreadPublicKey = changedThreadRecipient.getAddress().serialize();
}); for (String device : thisThreadAssociatedDevices) {
} if (device.equals(changedThreadPublicKey)) { isUpdateNeeded = true; }
return;
} }
}
this.updateInputPanel(); if (isUpdateNeeded) {
updateInputPanel();
}
} }
@Override @Override
public void handleSessionRestoreDevicesChanged(long threadId) { public void handleSessionRestoreDevicesChanged(long threadID) {
if (threadId == this.threadId) { if (threadID == this.threadId) {
runOnUiThread(this::updateSessionRestoreBanner); runOnUiThread(this::updateSessionRestoreBanner);
} }
} }
private void updateInputPanel() { private void updateInputPanel() {
/* boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(this, recipient);
isFriendsWithAnyDevice reflects whether we are friends with any of the other user's devices.
This fixes the case where the input panel disables and enables rapidly, which 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.
*/
if (recipient.isGroupRecipient() || isNoteToSelf() || isFriendsWithAnyDevice) { setInputPanelEnabled(true); return; }
// Disable the input panel if a friend request is pending
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
boolean isPending = friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED;
setInputPanelEnabled(!isPending);
// Always enable the input panel if we are friends with the current user
isFriendsWithAnyDevice = (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS);
if (!isFriendsWithAnyDevice) {
// 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 -> {
if (!hasPendingRequests) {
setInputPanelEnabled(true);
} else {
MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient).success( isFriends -> {
// Enable the input panel if we're friends with any of the user's devices
isFriendsWithAnyDevice = isFriends;
setInputPanelEnabled(isFriends);
return Unit.INSTANCE;
});
}
return Unit.INSTANCE;
});
}
}
private void setInputPanelEnabled(boolean enabled) {
Util.runOnMain(() -> { Util.runOnMain(() -> {
updateToggleButtonState(); updateToggleButtonState();
String hint = enabled ? "Message" : "Pending session request"; String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request";
inputPanel.setHint(hint); inputPanel.setHint(hint);
inputPanel.setEnabled(enabled); inputPanel.setEnabled(shouldInputPanelBeEnabled);
if (enabled && inputPanel.getVisibility() == View.VISIBLE) { if (shouldInputPanelBeEnabled && inputPanel.getVisibility() == View.VISIBLE) {
inputPanel.composeText.requestFocus(); inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0); inputMethodManager.showSoftInput(inputPanel.composeText, 0);
@ -2370,7 +2341,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.w(TAG, ex); Log.w(TAG, ex);
} }
if (messageStatus == null && !isGroupConversation()) { if (messageStatus == null && !isGroupConversation() && !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize())) {
messageStatus = "calculatingPoW"; messageStatus = "calculatingPoW";
updateSubtitleTextView(); updateSubtitleTextView();
updateMessageStatusProgressBar(); updateMessageStatusProgressBar();
@ -2425,7 +2396,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// Loki - Send a friend request if we're not yet friends with the user in question // Loki - Send a friend request if we're not yet friends with the user in question
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId); LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
outgoingMessage.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS; // Needed for stageOutgoingMessage(...) outgoingMessage.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
&& !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
if (clearComposeBox) { if (clearComposeBox) {
inputPanel.clearQuote(); inputPanel.clearQuote();
@ -2477,7 +2449,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// Loki - Send a friend request if we're not yet friends with the user in question // Loki - Send a friend request if we're not yet friends with the user in question
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId); LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
message.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS; // Needed for stageOutgoingMessage(...) message.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
&& !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
silentlySetComposeText(""); silentlySetComposeText("");
final long id = fragment.stageOutgoingMessage(message); final long id = fragment.stageOutgoingMessage(message);
@ -2508,8 +2481,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void updateToggleButtonState() { private void updateToggleButtonState() {
// Don't allow attachments if we're not friends with any of the user's devices if (!FriendRequestProtocol.shouldAttachmentButtonBeEnabled(this, recipient)) {
if (!isNoteToSelf() && !recipient.isGroupRecipient() && !isFriendsWithAnyDevice) {
buttonToggle.display(sendButton); buttonToggle.display(sendButton);
quickAttachmentToggle.hide(); quickAttachmentToggle.hide();
inlineAttachmentToggle.hide(); inlineAttachmentToggle.hide();
@ -2745,7 +2717,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void silentlySetComposeText(String text) { private void silentlySetComposeText(String text) {
typingTextWatcher.setEnabled(false); typingTextWatcher.setEnabled(false);
composeText.setText(text); composeText.setText(text);
if (text.isEmpty()) resetMentions(); if (text.isEmpty()) { resetMentions(); }
typingTextWatcher.setEnabled(true); typingTextWatcher.setEnabled(true);
} }
@ -2905,7 +2877,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this); LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this); LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this);
if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) { if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) {
List<Mention> mentionCandidates = LokiAPI.Companion.getMentionCandidates("", threadId, userHexEncodedPublicKey, threadDatabase, userDatabase); List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId);
currentMentionStartIndex = lastCharacterIndex; currentMentionStartIndex = lastCharacterIndex;
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId); mentionCandidateSelectionView.show(mentionCandidates, threadId);
@ -2916,7 +2888,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else { } else {
if (currentMentionStartIndex != -1) { if (currentMentionStartIndex != -1) {
String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @ String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @
List<Mention> mentionCandidates = LokiAPI.Companion.getMentionCandidates(query, threadId, userHexEncodedPublicKey, threadDatabase, userDatabase); List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId);
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId); mentionCandidateSelectionView.show(mentionCandidates, threadId);
} }
@ -2960,12 +2932,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
inputPanel.setQuote(GlideApp.with(this), inputPanel.setQuote(GlideApp.with(this),
messageRecord.getDateSent(), messageRecord.getDateSent(),
author, author,
body, body,
slideDeck, slideDeck,
recipient, recipient,
threadId); threadId);
} else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) {
LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
@ -2976,20 +2948,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
inputPanel.setQuote(GlideApp.with(this), inputPanel.setQuote(GlideApp.with(this),
messageRecord.getDateSent(), messageRecord.getDateSent(),
author, author,
messageRecord.getBody(), messageRecord.getBody(),
slideDeck, slideDeck,
recipient, recipient,
threadId); threadId);
} else { } else {
inputPanel.setQuote(GlideApp.with(this), inputPanel.setQuote(GlideApp.with(this),
messageRecord.getDateSent(), messageRecord.getDateSent(),
author, author,
messageRecord.getBody(), messageRecord.getBody(),
messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(),
recipient, recipient,
threadId); threadId);
} }
} }
@ -3106,17 +3078,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// region Loki // region Loki
private void updateTitleTextView(Recipient recipient) { private void updateTitleTextView(Recipient recipient) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userHexEncodedPublicKey); Set<String> allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
HashSet<String> userLinkedDeviceHexEncodedPublicKeys = new HashSet<>();
for (DeviceLink deviceLink : deviceLinks) {
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getMasterHexEncodedPublicKey().toLowerCase());
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getSlaveHexEncodedPublicKey().toLowerCase());
}
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase());
if (recipient == null) { if (recipient == null) {
titleTextView.setText("Compose"); titleTextView.setText("Compose");
} else if (userLinkedDeviceHexEncodedPublicKeys.contains(recipient.getAddress().toString().toLowerCase())) { } else if (allUserDevices.contains(recipient.getAddress().toString().toLowerCase())) {
titleTextView.setText("Note to Self"); titleTextView.setText("Note to Self");
} else { } else {
boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty());
@ -3192,7 +3158,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void handleMessageStatusChanged(String newMessageStatus, long timestamp) { private void handleMessageStatusChanged(String newMessageStatus, long timestamp) {
if (timestamp == 0) { return; } if (timestamp == 0 || SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()) ) { return; }
updateForNewMessageStatusIfNeeded(newMessageStatus, timestamp); updateForNewMessageStatusIfNeeded(newMessageStatus, timestamp);
if (newMessageStatus.equals("messageFailed") || newMessageStatus.equals("messageSent")) { if (newMessageStatus.equals("messageFailed") || newMessageStatus.equals("messageSent")) {
new Handler().postDelayed(() -> clearMessageStatusIfNeeded(timestamp), 1000); new Handler().postDelayed(() -> clearMessageStatusIfNeeded(timestamp), 1000);
@ -3232,48 +3198,16 @@ 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 if (recipient.isGroupRecipient()) { return; }
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this); FriendRequestProtocol.acceptFriendRequest(this, recipient);
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
Recipient contact = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
Address address = contact.getAddress();
String contactHexEncodedPublicKey = address.serialize();
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
DatabaseFactory.getRecipientDatabase(this).setProfileSharing(contact, true);
MessageSender.sendBackgroundMessageToAllDevices(this, contactHexEncodedPublicKey);
MessageSender.syncContact(this, address);
updateInputPanel(); updateInputPanel();
} }
@Override @Override
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) { public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.NONE);
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID).getAddress().toString();
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID);
updateInputPanel();
}
public boolean isNoteToSelf() {
return TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize());
}
public void restoreSession() {
if (recipient.isGroupRecipient()) { return; } if (recipient.isGroupRecipient()) { return; }
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(this); FriendRequestProtocol.rejectFriendRequest(this, recipient);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(this); updateInputPanel();
Set<String> devices = lokiThreadDatabase.getSessionRestoreDevices(threadId);
for (String device : devices) { MessageSender.sendRestoreSessionMessage(this, device); }
long messageID = smsDatabase.insertMessageOutbox(threadId, new OutgoingTextMessage(recipient,"", 0, 0), false, System.currentTimeMillis(), null);
if (messageID > -1) {
smsDatabase.markAsLokiSessionRestoreSent(messageID);
}
lokiThreadDatabase.removeAllSessionRestoreDevices(threadId);
updateSessionRestoreBanner();
} }
// endregion // endregion
} }

View File

@ -40,7 +40,7 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate; import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;

View File

@ -79,7 +79,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate; import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.longmessage.LongMessageActivity; import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
@ -101,8 +101,8 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -375,9 +375,9 @@ public class ConversationFragment extends Fragment
} }
if (messageRecords.size() > 1) { if (messageRecords.size() > 1) {
// menu.findItem(R.id.menu_context_forward).setVisible(false); // menu.findItem(R.id.menu_context_forward).setVisible(false);
menu.findItem(R.id.menu_context_reply).setVisible(false); menu.findItem(R.id.menu_context_reply).setVisible(false);
// menu.findItem(R.id.menu_context_details).setVisible(false); // menu.findItem(R.id.menu_context_details).setVisible(false);
menu.findItem(R.id.menu_context_save_attachment).setVisible(false); menu.findItem(R.id.menu_context_save_attachment).setVisible(false);
menu.findItem(R.id.menu_context_resend).setVisible(false); menu.findItem(R.id.menu_context_resend).setVisible(false);
} else { } else {
@ -726,7 +726,7 @@ public class ConversationFragment extends Fragment
} }
if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
// adapter.setHeaderView(unknownSenderView); // adapter.setHeaderView(unknownSenderView);
} else { } else {
clearHeaderIfNotTyping(adapter); clearHeaderIfNotTyping(adapter);
} }

View File

@ -86,11 +86,11 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; 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.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.redesign.utilities.MentionUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestView; import org.thoughtcrime.securesms.loki.views.FriendRequestView;
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate; import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView; import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
@ -112,8 +112,8 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub; import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;

View File

@ -49,8 +49,6 @@ public class ConversationPopupActivity extends ConversationActivity {
else getWindow().setLayout((int) (width * .7), (int) (height * .75)); else getWindow().setLayout((int) (width * .7), (int) (height * .75));
super.onCreate(bundle, ready); super.onCreate(bundle, ready);
// titleView.setOnClickListener(null);
} }
@Override @Override

View File

@ -105,16 +105,16 @@ public class ConversationUpdateItem extends LinearLayout
this.sender.addListener(this); this.sender.addListener(this);
if (messageRecord.isGroupAction()) setGroupRecord(messageRecord); if (messageRecord.isGroupAction()) setGroupRecord(messageRecord);
else if (messageRecord.isCallLog()) setCallRecord(messageRecord); else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord); else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord); else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord);
else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord); else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord);
else if (messageRecord.isIdentityVerified() || else if (messageRecord.isIdentityVerified() ||
messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord); messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord);
else if (messageRecord.isLokiSessionRestoreSent()) setTextMessageRecord(messageRecord); else if (messageRecord.isLokiSessionRestoreSent()) setTextMessageRecord(messageRecord);
else throw new AssertionError("Neither group nor log nor joined."); else throw new AssertionError("Neither group nor log nor joined.");
if (batchSelected.contains(messageRecord)) setSelected(true); if (batchSelected.contains(messageRecord)) setSelected(true);
else setSelected(false); else setSelected(false);

View File

@ -43,7 +43,7 @@ public class PreKeyUtil {
private static final int BATCH_SIZE = 100; private static final int BATCH_SIZE = 100;
public synchronized static List<PreKeyRecord> generatePreKeys(Context context) { public synchronized static List<PreKeyRecord> generatePreKeyRecords(Context context) {
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context); PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
List<PreKeyRecord> records = new LinkedList<>(); List<PreKeyRecord> records = new LinkedList<>();
int preKeyIdOffset = TextSecurePreferences.getNextPreKeyId(context); int preKeyIdOffset = TextSecurePreferences.getNextPreKeyId(context);
@ -101,7 +101,7 @@ public class PreKeyUtil {
} }
} }
public synchronized static List<PreKeyRecord> generatePreKeys(Context context, int amount) { public synchronized static List<PreKeyRecord> generatePreKeyRecords(Context context, int amount) {
List<PreKeyRecord> records = new LinkedList<>(); List<PreKeyRecord> records = new LinkedList<>();
int preKeyIDOffset = TextSecurePreferences.getNextPreKeyId(context); int preKeyIDOffset = TextSecurePreferences.getNextPreKeyId(context);
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {

View File

@ -9,22 +9,15 @@ import android.support.annotation.WorkerThread;
import org.signal.libsignal.metadata.SignalProtos; import org.signal.libsignal.metadata.SignalProtos;
import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.libsignal.metadata.certificate.CertificateValidator;
import org.signal.libsignal.metadata.certificate.InvalidCertificateException; import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
import network.loki.messenger.BuildConfig;
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.util.Base64;
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.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.IOException;
public class UnidentifiedAccessUtil { public class UnidentifiedAccessUtil {
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName(); private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
@ -116,9 +109,9 @@ public class UnidentifiedAccessUtil {
String ourNumber = TextSecurePreferences.getLocalNumber(context); String ourNumber = TextSecurePreferences.getLocalNumber(context);
if (ourNumber != null) { if (ourNumber != null) {
SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder() SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder()
.setSender(ourNumber) .setSender(ourNumber)
.setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID) .setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID)
.build(); .build();
return certificate.toByteArray(); return certificate.toByteArray();
} }

View File

@ -105,11 +105,11 @@ public class Address implements Parcelable, Comparable<Address> {
public boolean isGroup() { return GroupUtil.isEncodedGroup(address); } public boolean isGroup() { return GroupUtil.isEncodedGroup(address); }
public boolean isSignalGroup() { return GroupUtil.isSignalGroup(address); } public boolean isClosedGroup() { return GroupUtil.isClosedGroup(address); }
public boolean isPublicChat() { return GroupUtil.isPublicChat(address); } public boolean isOpenGroup() { return GroupUtil.isOpenGroup(address); }
public boolean isRSSFeed() { return GroupUtil.isRssFeed(address); } public boolean isRSSFeed() { return GroupUtil.isRSSFeed(address); }
public boolean isMmsGroup() { return GroupUtil.isMmsGroup(address); } public boolean isMmsGroup() { return GroupUtil.isMmsGroup(address); }
@ -127,7 +127,7 @@ public class Address implements Parcelable, Comparable<Address> {
} }
public @NonNull String toPhoneString() { public @NonNull String toPhoneString() {
if (!isPhone() && !isPublicChat()) { if (!isPhone() && !isOpenGroup()) {
if (isEmail()) throw new AssertionError("Not e164, is email"); if (isEmail()) throw new AssertionError("Not e164, is email");
if (isGroup()) throw new AssertionError("Not e164, is group"); if (isGroup()) throw new AssertionError("Not e164, is group");
throw new AssertionError("Not e164, unknown"); throw new AssertionError("Not e164, unknown");

View File

@ -31,11 +31,12 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.helpers.ClassicOpenHelper; import org.thoughtcrime.securesms.database.helpers.ClassicOpenHelper;
import org.thoughtcrime.securesms.database.helpers.SQLCipherMigrationHelper; import org.thoughtcrime.securesms.database.helpers.SQLCipherMigrationHelper;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.loki.*; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class DatabaseFactory { public class DatabaseFactory {

View File

@ -160,14 +160,15 @@ public class GroupDatabase extends Database {
return recipients; return recipients;
} }
public boolean signalGroupsHaveMember(String hexEncodedPublicKey) { public boolean isClosedGroupMember(String hexEncodedPublicKey) {
try { try {
Address address = Address.fromSerialized(hexEncodedPublicKey); Address address = Address.fromSerialized(hexEncodedPublicKey);
Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups(); Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups();
GroupRecord record; GroupRecord record;
while ((record = reader.getNext()) != null) { while ((record = reader.getNext()) != null) {
if (record.isSignalGroup() && record.members.contains(address)) { if (record.isClosedGroup() && record.members.contains(address)) {
return true; return true;
} }
} }
@ -292,8 +293,7 @@ public class GroupDatabase extends Database {
contents.put(ADMINS, Address.toSerializedList(admins, ',')); contents.put(ADMINS, Address.toSerializedList(admins, ','));
contents.put(ACTIVE, 1); contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId});
new String[] {groupId});
} }
public void remove(String groupId, Address source) { public void remove(String groupId, Address source) {
@ -489,11 +489,11 @@ public class GroupDatabase extends Database {
return mms; return mms;
} }
public boolean isPublicChat() { return Address.fromSerialized(id).isPublicChat(); } public boolean isOpenGroup() { return Address.fromSerialized(id).isOpenGroup(); }
public boolean isRSSFeed() { return Address.fromSerialized(id).isRSSFeed(); } public boolean isRSSFeed() { return Address.fromSerialized(id).isRSSFeed(); }
public boolean isSignalGroup() { return Address.fromSerialized(id).isSignalGroup(); } public boolean isClosedGroup() { return Address.fromSerialized(id).isClosedGroup(); }
public String getUrl() { return url; } public String getUrl() { return url; }

View File

@ -47,7 +47,6 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -252,7 +251,7 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT); updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT);
} }
public void markAsLokiSessionRestoreSent(long id) { public void markAsSentLokiSessionRestorationRequest(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT); updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT);
} }
@ -342,12 +341,12 @@ public class SmsDatabase extends MessagingDatabase {
try { try {
cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE }, cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE },
DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) }, DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) },
null, null, null, null); null, null, null, null);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
isOutgoing = true; isOutgoing = true;
} }
} }
} finally { } finally {

View File

@ -537,7 +537,7 @@ public class ThreadDatabase extends Database {
} }
public @Nullable Recipient getRecipientForThreadId(long threadId) { public @Nullable Recipient getRecipientForThreadId(long threadId) {
// Loki - Cache the address. // Loki - Cache the address
if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) { if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) {
return Recipient.from(context, addressCache.get(threadId), false); return Recipient.from(context, addressCache.get(threadId), false);
} }

View File

@ -35,17 +35,17 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import java.io.File; import java.io.File;
@ -81,8 +81,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV5 = 26; private static final int lokiV5 = 26;
private static final int lokiV6 = 27; private static final int lokiV6 = 27;
private static final int lokiV7 = 28; private static final int lokiV7 = 28;
private static final int lokiV8 = 29;
private static final int DATABASE_VERSION = lokiV7; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes private static final int DATABASE_VERSION = lokiV8; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final String DATABASE_NAME = "signal.db"; private static final String DATABASE_NAME = "signal.db";
private final Context context; private final Context context;
@ -138,6 +139,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand()); db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand());
db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand());
db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand()); db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand());
@ -181,8 +183,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
@Override @Override
public void onConfigure(SQLiteDatabase db) { public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db); super.onConfigure(db);
// Loki: Enable Write Ahead Logging Mode, increase the cache size // Loki - Enable write ahead logging mode and increase the cache size.
// This should be disabled if we ever run into serious race condition bugs // This should be disabled if we ever run into serious race condition bugs.
db.enableWriteAheadLogging(); db.enableWriteAheadLogging();
db.execSQL("PRAGMA cache_size = 10000"); db.execSQL("PRAGMA cache_size = 10000");
} }
@ -544,7 +546,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
if (publicChat != null) { if (publicChat != null) {
byte[] groupId = publicChat.getId().getBytes(); byte[] groupId = publicChat.getId().getBytes();
String oldId = GroupUtil.getEncodedId(groupId, false); String oldId = GroupUtil.getEncodedId(groupId, false);
String newId = GroupUtil.getEncodedPublicChatId(groupId); String newId = GroupUtil.getEncodedOpenGroupId(groupId);
ContentValues threadUpdate = new ContentValues(); ContentValues threadUpdate = new ContentValues();
threadUpdate.put("recipient_ids", newId); threadUpdate.put("recipient_ids", newId);
db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId }); db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId });
@ -555,7 +557,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
} }
} }
// Migrate rss feeds from __textsecure_group__ to __loki_rss_feed_group__ // Migrate RSS feeds from __textsecure_group__ to __loki_rss_feed_group__
String[] rssFeedIds = new String[] { "loki.network.feed", "loki.network.messenger-updates.feed" }; String[] rssFeedIds = new String[] { "loki.network.feed", "loki.network.messenger-updates.feed" };
for (String groupId : rssFeedIds) { for (String groupId : rssFeedIds) {
String oldId = GroupUtil.getEncodedId(groupId.getBytes(), false); String oldId = GroupUtil.getEncodedId(groupId.getBytes(), false);
@ -576,6 +578,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
} }
if (oldVersion < lokiV8) {
db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampTableCommand());
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View File

@ -8,11 +8,11 @@ import com.annimon.stream.Stream;
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.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.loki.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.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import java.io.File; import java.io.File;
import java.util.Collections; import java.util.Collections;
@ -33,9 +33,9 @@ public class DeviceListLoader extends AsyncLoader<List<Device>> {
@Override @Override
public List<Device> loadInBackground() { public List<Device> loadInBackground() {
try { try {
String ourPublicKey = TextSecurePreferences.getLocalNumber(getContext()); String userPublicKey = TextSecurePreferences.getLocalNumber(getContext());
Set<String> secondaryDevicePublicKeys = LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(ourPublicKey).get(); Set<String> slaveDevicePublicKeys = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey);
List<Device> devices = Stream.of(secondaryDevicePublicKeys).map(this::mapToDevice).toList(); List<Device> devices = Stream.of(slaveDevicePublicKeys).map(this::mapToDevice).toList();
Collections.sort(devices, new DeviceComparator()); Collections.sort(devices, new DeviceComparator());
return devices; return devices;
} catch (Exception e) { } catch (Exception e) {

View File

@ -21,7 +21,6 @@ import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.SpannableString; import android.text.SpannableString;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@ -30,6 +29,8 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import network.loki.messenger.R;
/** /**
* The message record model which represents standard SMS messages. * The message record model which represents standard SMS messages.
* *
@ -67,9 +68,9 @@ public class SmsMessageRecord extends MessageRecord {
int readReceiptCount, boolean unidentified, boolean isFriendRequest) int readReceiptCount, boolean unidentified, boolean isFriendRequest)
{ {
super(id, body, recipient, individualRecipient, recipientDeviceId, super(id, body, recipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, status, deliveryReceiptCount, type, dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
mismatches, new LinkedList<>(), subscriptionId, mismatches, new LinkedList<>(), subscriptionId,
expiresIn, expireStarted, readReceiptCount, unidentified); expiresIn, expireStarted, readReceiptCount, unidentified);
this.isFriendRequest = isFriendRequest; this.isFriendRequest = isFriendRequest;
} }

View File

@ -45,9 +45,8 @@ 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.protocol.LokiSessionResetImplementation;
import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
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;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
@ -112,7 +111,6 @@ import network.loki.messenger.BuildConfig;
MultiDeviceStickerPackOperationJob.class, MultiDeviceStickerPackOperationJob.class,
MultiDeviceStickerPackSyncJob.class, MultiDeviceStickerPackSyncJob.class,
LinkPreviewRepository.class, LinkPreviewRepository.class,
PushMessageSyncSendJob.class,
MultiDeviceOpenGroupUpdateJob.class}) MultiDeviceOpenGroupUpdateJob.class})
public class SignalCommunicationModule { public class SignalCommunicationModule {
@ -154,6 +152,7 @@ public class SignalCommunicationModule {
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()), Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
Optional.of(new MessageSenderEventListener(context)), Optional.of(new MessageSenderEventListener(context)),
TextSecurePreferences.getLocalNumber(context), TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getMasterHexEncodedPublicKey(context),
DatabaseFactory.getLokiAPIDatabase(context), DatabaseFactory.getLokiAPIDatabase(context),
DatabaseFactory.getLokiThreadDatabase(context), DatabaseFactory.getLokiThreadDatabase(context),
DatabaseFactory.getLokiMessageDatabase(context), DatabaseFactory.getLokiMessageDatabase(context),

View File

@ -36,18 +36,18 @@ import java.util.Set;
public class GroupManager { public class GroupManager {
public static long getPublicChatThreadId(String id, @NonNull Context context) { public static long getOpenGroupThreadID(String id, @NonNull Context context) {
final String groupId = GroupUtil.getEncodedPublicChatId(id.getBytes()); final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes());
return getThreadIdFromGroupId(groupId, context); return getThreadIDFromGroupID(groupID, context);
} }
public static long getRSSFeedThreadId(String id, @NonNull Context context) { public static long getRSSFeedThreadID(String id, @NonNull Context context) {
final String groupId = GroupUtil.getEncodedRSSFeedId(id.getBytes()); final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes());
return getThreadIdFromGroupId(groupId, context); return getThreadIDFromGroupID(groupID, context);
} }
public static long getThreadIdFromGroupId(String groupId, @NonNull Context context) { public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) {
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false); final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupID), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
} }
@ -78,10 +78,10 @@ public class GroupManager {
final Set<Address> memberAddresses = getMemberAddresses(members); final Set<Address> memberAddresses = getMemberAddresses(members);
final Set<Address> adminAddresses = getMemberAddresses(admins); final Set<Address> adminAddresses = getMemberAddresses(admins);
String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
String ourNumber = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context); String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context);
memberAddresses.add(Address.fromSerialized(ourNumber)); memberAddresses.add(Address.fromSerialized(masterPublicKey));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses)); groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses));
if (!mms) { if (!mms) {
@ -94,28 +94,28 @@ public class GroupManager {
} }
} }
public static @NonNull GroupActionResult createPublicChatGroup(@NonNull String id, public static @NonNull GroupActionResult createOpenGroup(@NonNull String id,
@NonNull Context context, @NonNull Context context,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name) @Nullable String name)
{ {
final String groupId = GroupUtil.getEncodedPublicChatId(id.getBytes()); final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes());
return createLokiGroup(groupId, context, avatar, name); return createLokiGroup(groupID, context, avatar, name);
} }
public static @NonNull GroupActionResult createRSSFeedGroup(@NonNull String id, public static @NonNull GroupActionResult createRSSFeed(@NonNull String id,
@NonNull Context context, @NonNull Context context,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name) @Nullable String name)
{ {
final String groupId = GroupUtil.getEncodedRSSFeedId(id.getBytes()); final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes());
return createLokiGroup(groupId, context, avatar, name); return createLokiGroup(groupID, context, avatar, name);
} }
private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId, private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId,
@NonNull Context context, @NonNull Context context,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name) @Nullable String name)
{ {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
@ -127,8 +127,8 @@ public class GroupManager {
groupDatabase.updateAvatar(groupId, avatarBytes); groupDatabase.updateAvatar(groupId, avatarBytes);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId); return new GroupActionResult(groupRecipient, threadID);
} }
public static GroupActionResult updateGroup(@NonNull Context context, public static GroupActionResult updateGroup(@NonNull Context context,

View File

@ -8,7 +8,6 @@ import android.support.annotation.Nullable;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
@ -18,26 +17,23 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
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.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; 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.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -45,8 +41,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import kotlin.Unit;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
@ -95,7 +89,7 @@ public class GroupMessageProcessor {
builder.setType(GroupContext.Type.UPDATE); builder.setType(GroupContext.Type.UPDATE);
SignalServiceAttachment avatar = group.getAvatar().orNull(); SignalServiceAttachment avatar = group.getAvatar().orNull();
List<Address> members = group.getMembers().isPresent() ? new LinkedList<Address>() : null; List<Address> members = group.getMembers().isPresent() ? new LinkedList<>() : null;
List<Address> admins = group.getAdmins().isPresent() ? new LinkedList<>() : null; List<Address> admins = group.getAdmins().isPresent() ? new LinkedList<>() : null;
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
@ -104,13 +98,12 @@ public class GroupMessageProcessor {
} }
} }
// We should only create the group if we are part of the member list // Loki - Ignore message if needed
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context)); if (ClosedGroupsProtocol.shouldIgnoreGroupCreatedMessage(context, group)) {
if (members == null || !members.contains(Address.fromSerialized(hexEncodedPublicKey))) {
Log.d("Loki - Group Message", "Received a group create message which doesn't include us in the member list. Ignoring.");
return null; return null;
} }
// Loki - Parse admins
if (group.getAdmins().isPresent()) { if (group.getAdmins().isPresent()) {
for (String admin : group.getAdmins().get()) { for (String admin : group.getAdmins().get()) {
admins.add(Address.fromExternal(context, admin)); admins.add(Address.fromExternal(context, admin));
@ -121,7 +114,7 @@ public class GroupMessageProcessor {
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins); avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins);
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
} }
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
@ -137,21 +130,21 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group); String id = GroupUtil.getEncodedId(group);
String ourHexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context)); String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
// Only update group if the group admin sent the message // Loki - Only update the group if the group admin sent the message
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender()); String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
if (!groupRecord.getAdmins().contains(Address.fromSerialized(hexEncodedPublicKey))) { if (!groupRecord.getAdmins().contains(Address.fromSerialized(masterDevice))) {
Log.d("Loki - Group Message", "Received a group update message from a non-admin user for " + id +". Ignoring."); Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring.");
return null; return null;
} }
// We should only process update messages if we're in the group // Loki - Only process update messages if we're part of the group
Address ourAddress = Address.fromSerialized(ourHexEncodedPublicKey); Address userMasterDeviceAddress = Address.fromSerialized(userMasterDevice);
if (!groupRecord.getMembers().contains(ourAddress) && if (!groupRecord.getMembers().contains(userMasterDeviceAddress) &&
!group.getMembers().or(Collections.emptyList()).contains(ourHexEncodedPublicKey)) { !group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) {
Log.d("Loki - Group Message", "Received a group update message from a group we are not a member of: " + id + "; ignoring."); Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring.");
database.setActive(id, false); database.setActive(id, false);
return null; return null;
} }
@ -180,8 +173,8 @@ public class GroupMessageProcessor {
database.updateMembers(id, new LinkedList<>(newMembers)); database.updateMembers(id, new LinkedList<>(newMembers));
} }
// We add any new or removed members to the group context // Add any new or removed members to the group context.
// This will allow us later to iterate over them to check if they left or were added for UI purposes // This will allow us later to iterate over them to check if they left or were added for UI purposes.
for (Address addedMember : addedMembers) { for (Address addedMember : addedMembers) {
builder.addNewMembers(addedMember.serialize()); builder.addNewMembers(addedMember.serialize());
} }
@ -200,13 +193,13 @@ public class GroupMessageProcessor {
} }
// If we were removed then we need to disable the chat // If we were removed then we need to disable the chat
if (removedMembers.contains(Address.fromSerialized(ourHexEncodedPublicKey))) { if (removedMembers.contains(Address.fromSerialized(userMasterDevice))) {
database.setActive(id, false); database.setActive(id, false);
} else { } else {
if (!groupRecord.isActive()) database.setActive(id, true); if (!groupRecord.isActive()) database.setActive(id, true);
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
establishSessionsWithMembersIfNeeded(context, group.getMembers().get()); ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
} }
} }
@ -218,8 +211,8 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
@NonNull GroupRecord record) @NonNull GroupRecord record)
{ {
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender()); String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
if (record.getMembers().contains(Address.fromSerialized(hexEncodedPublicKey))) { if (record.getMembers().contains(Address.fromSerialized(masterDevice))) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId())); .add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
@ -240,9 +233,9 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT); builder.setType(GroupContext.Type.QUIT);
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender()); String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
if (members.contains(Address.fromExternal(context, hexEncodedPublicKey))) { if (members.contains(Address.fromExternal(context, masterDevice))) {
database.remove(id, Address.fromExternal(context, hexEncodedPublicKey)); database.remove(id, Address.fromExternal(context, masterDevice));
if (outgoing) database.setActive(id, false); if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
@ -322,32 +315,4 @@ public class GroupMessageProcessor {
return builder; return builder;
} }
private static String getMasterHexEncodedPublicKey(Context context, String hexEncodedPublicKey) {
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
try {
String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey)
? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
: PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey;
} catch (Exception e) {
return hexEncodedPublicKey;
}
}
private static void establishSessionsWithMembersIfNeeded(Context context, List<String> members) {
String ourNumber = TextSecurePreferences.getLocalNumber(context);
for (String member : members) {
// Make sure we have session with all of the members secondary devices
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(member).success(devices -> {
if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
for (String device : devices) {
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);
boolean haveSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
if (!haveSession) { MessageSender.sendBackgroundSessionRequest(context, device); }
}
return Unit.INSTANCE;
});
}
}
} }

View File

@ -196,14 +196,17 @@ public class AttachmentDownloadJob extends BaseJob implements InjectableType {
try { try {
long id = Long.parseLong(attachment.getLocation()); long id = Long.parseLong(attachment.getLocation());
if (isPublicAttachment) { if (isPublicAttachment) {
return new SignalServiceAttachmentPointer(id, null, new byte[0], return new SignalServiceAttachmentPointer(id,
Optional.of(Util.toIntExact(attachment.getSize())), null,
Optional.absent(), new byte[0],
0, 0, Optional.of(Util.toIntExact(attachment.getSize())),
Optional.fromNullable(attachment.getDigest()), Optional.absent(),
Optional.fromNullable(attachment.getFileName()), 0,
attachment.isVoiceNote(), 0,
Optional.absent(), attachment.getUrl()); Optional.fromNullable(attachment.getDigest()),
Optional.fromNullable(attachment.getFileName()),
attachment.isVoiceNote(),
Optional.absent(), attachment.getUrl());
} }
byte[] key = Base64.decode(attachment.getKey()); byte[] key = Base64.decode(attachment.getKey());

View File

@ -40,7 +40,7 @@ public class CleanPreKeysJob extends BaseJob implements InjectableType {
public CleanPreKeysJob() { public CleanPreKeysJob() {
this(new Job.Parameters.Builder() this(new Job.Parameters.Builder()
.setQueue("CleanPreKeysJob") .setQueue("CleanPreKeysJob")
.setMaxAttempts(5) .setMaxAttempts(3)
.build()); .build());
} }

View File

@ -13,9 +13,8 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -71,8 +70,7 @@ public final class JobManagerFactories {
put(TrimThreadJob.KEY, new TrimThreadJob.Factory()); put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
put(TypingSendJob.KEY, new TypingSendJob.Factory()); put(TypingSendJob.KEY, new TypingSendJob.Factory());
put(UpdateApkJob.KEY, new UpdateApkJob.Factory()); put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory()); put(PushEphemeralMessageSendJob.KEY, new PushEphemeralMessageSendJob.Factory());
put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory());
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory()); put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
}}; }};
} }

View File

@ -3,11 +3,6 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import com.android.mms.dom.smil.parser.SmilXmlSerializer; import com.android.mms.dom.smil.parser.SmilXmlSerializer;
@ -30,6 +25,10 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
@ -64,7 +63,7 @@ public class MmsSendJob extends SendJob {
this(new Job.Parameters.Builder() this(new Job.Parameters.Builder()
.setQueue("mms-operation") .setQueue("mms-operation")
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(15) .setMaxAttempts(25)
.build(), .build(),
messageId); messageId);
} }

View File

@ -84,8 +84,7 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob implements InjectableTy
} }
} }
// TODO: Message ID messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
messageSender.sendMessage(0, SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
UnidentifiedAccessUtil.getAccessForSync(context)); UnidentifiedAccessUtil.getAccessForSync(context));
} }
} }

View File

@ -91,8 +91,7 @@ public class MultiDeviceConfigurationUpdateJob extends BaseJob implements Inject
return; return;
} }
// TODO: Message ID messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled),
messageSender.sendMessage(0, SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled),
Optional.of(unidentifiedDeliveryIndicatorsEnabled), Optional.of(unidentifiedDeliveryIndicatorsEnabled),
Optional.of(typingIndicatorsEnabled), Optional.of(typingIndicatorsEnabled),
Optional.of(linkPreviewsEnabled))), Optional.of(linkPreviewsEnabled))),

View File

@ -1,21 +1,16 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.jobs;
import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
@ -23,13 +18,12 @@ import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol;
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.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -39,18 +33,13 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -65,57 +54,47 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6); private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6);
private static final String KEY_ADDRESS = "address"; private static final String KEY_ADDRESS = "address";
private static final String KEY_RECIPIENT = "recipient";
private static final String KEY_FORCE_SYNC = "force_sync"; private static final String KEY_FORCE_SYNC = "force_sync";
@Inject SignalServiceMessageSender messageSender; @Inject SignalServiceMessageSender messageSender;
private @Nullable String address; private @Nullable String address;
// The recipient of this sync message. If null then we send to all devices
private @Nullable String recipient;
private boolean forceSync; private boolean forceSync;
/** /**
* Create a full contact sync job which syncs across to all other devices * Create a full contact sync job that syncs to all linked devices.
*/ */
public MultiDeviceContactUpdateJob(@NonNull Context context) { public MultiDeviceContactUpdateJob(@NonNull Context context) {
this(context, false); this(context, false);
} }
public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) { this(context, null, forceSync); }
/** public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) {
* Create a full contact sync job which only gets sent to `recipient` this(context, null, forceSync);
*/
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address recipient, boolean forceSync) {
this(context, recipient, null, forceSync);
} }
/** /**
* Create a single contact sync job which syncs across `address` to the all other devices * Create a single contact sync job that syncs `address` to all linked devices.
*/ */
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) { public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) {
this(context, null, address, true); this(context, address, true);
} }
private MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address recipient, @Nullable Address address, boolean forceSync) { public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) {
this(new Job.Parameters.Builder() this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setQueue("MultiDeviceContactUpdateJob") .setQueue("MultiDeviceContactUpdateJob")
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(1) .setMaxAttempts(1)
.build(), .build(),
recipient,
address, address,
forceSync); forceSync);
} }
private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address recipient, @Nullable Address address, boolean forceSync) { private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) {
super(parameters); super(parameters);
this.forceSync = forceSync; this.forceSync = forceSync;
this.recipient = (recipient != null) ? recipient.serialize() : null;
if (address != null) this.address = address.serialize(); if (address != null) this.address = address.serialize();
else this.address = null; else this.address = null;
@ -125,7 +104,6 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_ADDRESS, address) return new Data.Builder().putString(KEY_ADDRESS, address)
.putBoolean(KEY_FORCE_SYNC, forceSync) .putBoolean(KEY_FORCE_SYNC, forceSync)
.putString(KEY_RECIPIENT, recipient)
.build(); .build();
} }
@ -144,14 +122,14 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
} }
if (address == null) generateFullContactUpdate(); if (address == null) generateFullContactUpdate();
else if (!address.equals(TextSecurePreferences.getMasterHexEncodedPublicKey(context))) generateSingleContactUpdate(Address.fromSerialized(address)); else if (SyncMessagesProtocol.shouldSyncContact(context, Address.fromSerialized(address))) generateSingleContactUpdate(Address.fromSerialized(address));
} }
private void generateSingleContactUpdate(@NonNull Address address) private void generateSingleContactUpdate(@NonNull Address address)
throws IOException, UntrustedIdentityException, NetworkException throws IOException, UntrustedIdentityException, NetworkException
{ {
// Loki - Only sync regular contacts // Loki - Only sync regular contacts
if (!address.isPhone()) { return; } if (!PublicKeyValidation.isValid(address.serialize())) { return; }
File contactDataFile = createTempFile("multidevice-contact-update"); File contactDataFile = createTempFile("multidevice-contact-update");
@ -162,17 +140,15 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord); Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
// Loki - Only sync contacts we are friends with // Loki - Only sync contacts we are friends with
if (getFriendRequestStatus(recipient) == LokiThreadFriendRequestStatus.FRIENDS) { if (SyncMessagesProtocol.shouldSyncContact(context, address)) {
out.write(new DeviceContact(address.toPhoneString(), out.write(new DeviceContact(address.toPhoneString(),
Optional.fromNullable(recipient.getName()), Optional.fromNullable(recipient.getName()),
getAvatar(recipient.getContactUri()), getAvatar(recipient.getContactUri()),
Optional.fromNullable(recipient.getColor().serialize()), Optional.fromNullable(recipient.getColor().serialize()),
verifiedMessage, verifiedMessage,
Optional.fromNullable(recipient.getProfileKey()), Optional.fromNullable(recipient.getProfileKey()),
recipient.isBlocked(), recipient.isBlocked(),
recipient.getExpireMessages() > 0 ? recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent()));
Optional.of(recipient.getExpireMessages()) :
Optional.absent()));
} }
out.close(); out.close();
@ -206,7 +182,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
try { try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile)); DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
List<ContactData> contacts = getAllContacts(); List<ContactData> contacts = SyncMessagesProtocol.getContactsToSync(context);
for (ContactData contactData : contacts) { for (ContactData contactData : contacts) {
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id)); Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id));
@ -220,10 +196,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
boolean blocked = recipient.isBlocked(); boolean blocked = recipient.isBlocked();
Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent(); Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
// Loki - Only sync contacts we are friends with out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
if (getFriendRequestStatus(recipient) == LokiThreadFriendRequestStatus.FRIENDS) {
out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
}
} }
if (ProfileKeyUtil.hasProfileKey(context)) { if (ProfileKeyUtil.hasProfileKey(context)) {
@ -244,29 +217,9 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
} }
} }
private List<ContactData> getAllContacts() {
List<Address> contactAddresses = new ArrayList<>(DatabaseFactory.getRecipientDatabase(context).getAllAddresses());
List<ContactData> contacts = new ArrayList<>(contactAddresses.size());
for (Address address : contactAddresses) {
if (!address.isPhone()) { continue; }
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, address, false));
String name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(address.serialize());
ContactData contactData = new ContactData(threadId, name);
contactData.numbers.add(new ContactAccessor.NumberData("TextSecure", address.serialize()));
contacts.add(contactData);
}
return contacts;
}
private LokiThreadFriendRequestStatus getFriendRequestStatus(Recipient recipient) {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
return DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
}
@Override @Override
public boolean onShouldRetry(@NonNull Exception exception) { public boolean onShouldRetry(@NonNull Exception exception) {
// Loki - Disabled because we have our own retrying // Loki - Disabled since we have our own retrying
// if (exception instanceof PushNetworkException) return true;
return false; return false;
} }
@ -286,13 +239,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
.withLength(contactsFile.length()) .withLength(contactsFile.length())
.build(); .build();
SignalServiceAddress messageRecipient = recipient != null ? new SignalServiceAddress(recipient) : null; Optional<UnidentifiedAccessPair> unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)) : Optional.absent();
Address address = recipient != null ? Address.fromSerialized(recipient) : null;
Optional<UnidentifiedAccessPair> unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, address, false)) : Optional.absent();
try { try {
messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess, Optional.fromNullable(messageRecipient)); messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new NetworkException(ioe); throw new NetworkException(ioe);
} }
@ -302,7 +252,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
private Optional<SignalServiceAttachmentStream> getAvatar(@Nullable Uri uri) throws IOException { private Optional<SignalServiceAttachmentStream> getAvatar(@Nullable Uri uri) throws IOException {
return Optional.absent(); return Optional.absent();
/* Loki - Disabled until we support custom avatars. This will need to be reworked /* Loki - Disabled until we support custom profile pictures. This will then need to be reworked.
if (uri == null) { if (uri == null) {
return Optional.absent(); return Optional.absent();
} }
@ -397,10 +347,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
String serialized = data.getString(KEY_ADDRESS); String serialized = data.getString(KEY_ADDRESS);
Address address = serialized != null ? Address.fromSerialized(serialized) : null; Address address = serialized != null ? Address.fromSerialized(serialized) : null;
String recipientSerialized = data.getString(KEY_RECIPIENT); return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC));
Address recipient = recipientSerialized != null ? Address.fromSerialized(recipientSerialized) : null;
return new MultiDeviceContactUpdateJob(parameters, recipient, address, data.getBoolean(KEY_FORCE_SYNC));
} }
} }
} }

View File

@ -23,7 +23,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -85,7 +84,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
reader = DatabaseFactory.getGroupDatabase(context).getGroups(); reader = DatabaseFactory.getGroupDatabase(context).getGroups();
while ((record = reader.getNext()) != null) { while ((record = reader.getNext()) != null) {
if (record.isSignalGroup()) { if (record.isClosedGroup()) {
List<String> members = new LinkedList<>(); List<String> members = new LinkedList<>();
List<String> admins = new LinkedList<>(); List<String> admins = new LinkedList<>();
@ -125,8 +124,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
@Override @Override
public boolean onShouldRetry(@NonNull Exception exception) { public boolean onShouldRetry(@NonNull Exception exception) {
// Loki - Disabled because we have our own retrying // Loki - Disabled since we have our own retrying
// if (exception instanceof PushNetworkException) return true;
return false; return false;
} }
@ -145,7 +143,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
.withLength(contactsFile.length()) .withLength(contactsFile.length())
.build(); .build();
messageSender.sendMessage(0, SignalServiceSyncMessage.forGroups(attachmentStream), messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream),
UnidentifiedAccessUtil.getAccessForSync(context)); UnidentifiedAccessUtil.getAccessForSync(context));
} }

View File

@ -87,10 +87,9 @@ public class MultiDeviceProfileKeyUpdateJob extends BaseJob implements Injectabl
.withLength(baos.toByteArray().length) .withLength(baos.toByteArray().length)
.build(); .build();
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false)); SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
// TODO: Message ID messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
messageSender.sendMessage(0, syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
} }
@Override @Override

View File

@ -94,8 +94,7 @@ public class MultiDeviceReadUpdateJob extends BaseJob implements InjectableType
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp)); readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
} }
// TODO: Message ID messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
messageSender.sendMessage(0, SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
} }
@Override @Override

View File

@ -2,20 +2,14 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
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.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
@ -76,6 +70,7 @@ public class MultiDeviceStickerPackOperationJob extends BaseJob implements Injec
@Override @Override
protected void onRun() throws Exception { protected void onRun() throws Exception {
/*
if (!TextSecurePreferences.isMultiDevice(context)) { if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting..."); Log.i(TAG, "Not multi device, aborting...");
return; return;
@ -94,8 +89,9 @@ public class MultiDeviceStickerPackOperationJob extends BaseJob implements Injec
StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType); StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType);
messageSender.sendMessage(0, SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)), // The message ID doesn't matter messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)),
UnidentifiedAccessUtil.getAccessForSync(context)); UnidentifiedAccessUtil.getAccessForSync(context));
*/
} }
@Override @Override

View File

@ -2,24 +2,14 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.StickerDatabase.StickerPackRecordReader;
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
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.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
@ -59,6 +49,8 @@ public class MultiDeviceStickerPackSyncJob extends BaseJob implements Injectable
@Override @Override
protected void onRun() throws Exception { protected void onRun() throws Exception {
return;
/*
if (!TextSecurePreferences.isMultiDevice(context)) { if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting..."); Log.i(TAG, "Not multi device, aborting...");
return; return;
@ -76,8 +68,9 @@ public class MultiDeviceStickerPackSyncJob extends BaseJob implements Injectable
} }
} }
messageSender.sendMessage(0, SignalServiceSyncMessage.forStickerPackOperations(operations), // The message ID doesn't matter messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(operations),
UnidentifiedAccessUtil.getAccessForSync(context)); UnidentifiedAccessUtil.getAccessForSync(context));
*/
} }
@Override @Override

View File

@ -3,23 +3,16 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus; import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -89,6 +82,7 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableT
@Override @Override
public void onRun() throws IOException, UntrustedIdentityException { public void onRun() throws IOException, UntrustedIdentityException {
/*
try { try {
if (!TextSecurePreferences.isMultiDevice(context)) { if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device..."); Log.i(TAG, "Not multi device...");
@ -104,12 +98,12 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableT
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus); VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp); VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
// TODO: Message ID messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
messageSender.sendMessage(0, SignalServiceSyncMessage.forVerified(verifiedMessage),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false))); UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false)));
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
throw new IOException(e); throw new IOException(e);
} }
*/
} }
private VerifiedMessage.VerifiedState getVerifiedState(VerifiedStatus status) { private VerifiedMessage.VerifiedState getVerifiedState(VerifiedStatus status) {

File diff suppressed because it is too large Load Diff

View File

@ -12,31 +12,26 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.groups.GroupManager;
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.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@ -50,14 +45,10 @@ 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.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -159,36 +150,13 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
try { try {
log(TAG, "Sending message: " + messageId); log(TAG, "Sending message: " + messageId);
List<Address> target; List<Address> targets;
if (filterAddress != null) target = Collections.singletonList(Address.fromSerialized(filterAddress)); if (filterAddress != null) targets = Collections.singletonList(Address.fromSerialized(filterAddress));
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList(); else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
else target = getGroupMessageRecipients(message.getRecipient().getAddress().toGroupString(), messageId); else targets = ClosedGroupsProtocol.getDestinations(message.getRecipient().getAddress().toGroupString(), context).get();
String localNumber = TextSecurePreferences.getLocalNumber(context); List<SendMessageResult> results = deliver(message, targets);
// Only send messages to the contacts we have sessions with
List<Address> validTargets = Stream.of(target).filter(member -> {
if (member.isPublicChat()) { return true; }
// Our device is always valid
if (member.serialize().equalsIgnoreCase(localNumber)) { return true; }
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(member.toPhoneString(), SignalServiceAddress.DEFAULT_DEVICE_ID);
boolean hasSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
if (hasSession) { return true; }
// We should allow sending if we have a prekeybundle for the contact
return DatabaseFactory.getLokiPreKeyBundleDatabase(context).hasPreKeyBundle(member.toPhoneString());
}).toList();
// Send a session request to the other devices
List<Address> others = Stream.of(target).filter(t -> !validTargets.contains(t)).toList();
for (Address device : others) {
MessageSender.sendBackgroundSessionRequest(context, device.toPhoneString());
}
List<SendMessageResult> results = deliver(message, validTargets);
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList(); List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList(); List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet()); Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
@ -237,7 +205,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId); notifyMediaMessageDeliveryFailed(context, messageId);
} }
} catch (UntrustedIdentityException | UndeliverableMessageException e) { } catch (Exception e) {
warn(TAG, e); warn(TAG, e);
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId); notifyMediaMessageDeliveryFailed(context, messageId);
@ -246,10 +214,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Override @Override
public boolean onShouldRetry(@NonNull Exception exception) { public boolean onShouldRetry(@NonNull Exception exception) {
if (exception instanceof IOException) return true; if (exception instanceof IOException) return true;
// Loki - Disable since we have our own retrying // Loki - Disable since we have our own retrying
// if (exception instanceof RetryLaterException) return true;
return false; return false;
} }
@ -260,17 +226,18 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations) private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations)
throws IOException, UntrustedIdentityException, UndeliverableMessageException { throws IOException, UntrustedIdentityException, UndeliverableMessageException {
// rotateSenderCertificateIfNecessary();
// Messages shouldn't be able to be sent to RSS Feeds // Loki - The user shouldn't be able to message RSS feeds
Address groupAddress = message.getRecipient().getAddress(); Address address = message.getRecipient().getAddress();
if (groupAddress.isRSSFeed()) { if (address.isRSSFeed()) {
List<SendMessageResult> results = new ArrayList<>(); List<SendMessageResult> results = new ArrayList<>();
for (Address destination : destinations) results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString()))); for (Address destination : destinations) {
results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString())));
}
return results; return results;
} }
String groupId = groupAddress.toGroupString(); String groupId = address.toGroupString();
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
Optional<Quote> quote = getQuoteFor(message); Optional<Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message); Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
@ -281,18 +248,15 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments); List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses) List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
.map(address -> Address.fromSerialized(address.getNumber())) .map(a -> Address.fromSerialized(a.getNumber()))
.map(address -> Recipient.from(context, address, false)) .map(a -> Recipient.from(context, a, false))
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList(); .toList();
SignalServiceGroup.GroupType groupType = SignalServiceGroup.GroupType.SIGNAL; SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL;
if (groupAddress.isPublicChat()) {
groupType = SignalServiceGroup.GroupType.PUBLIC_CHAT;
}
if (message.isGroup() && groupAddress.isSignalGroup()) { if (message.isGroup() && address.isClosedGroup()) {
// Loki - Only send GroupUpdate or GroupQuit to signal groups // Loki - Only send GroupUpdate or GroupQuit messages to closed groups
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message; OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
GroupContext groupContext = groupMessage.getGroupContext(); GroupContext groupContext = groupMessage.getGroupContext();
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0); SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
@ -301,7 +265,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis()) .withTimestamp(message.getSentTimeMillis())
.withExpiration(message.getRecipient().getExpireMessages()) .withExpiration(message.getRecipient().getExpireMessages())
.withBody(message.getBody())
.asGroupMessage(group) .asGroupMessage(group)
.build(); .build();
@ -326,65 +289,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
} }
} }
private @NonNull List<Address> getGroupMessageRecipients(String groupId, long messageId) {
if (GroupUtil.isRssFeed(groupId)) { return new ArrayList<>(); }
// Loki - All public chat group messages should be directed to their respective servers
if (GroupUtil.isPublicChat(groupId)) {
ArrayList<Address> result = new ArrayList<>();
long threadID = GroupManager.getThreadIdFromGroupId(groupId, context);
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID);
if (publicChat != null) {
result.add(Address.fromSerialized(groupId));
}
return result;
} else {
/*
Our biggest assumption here is that group members will only consist of primary devices.
No secondary device should be able to be added to a group.
*/
List<GroupReceiptInfo> destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
Set<Address> memberSet = new HashSet<>();
if (destinations.isEmpty()) {
List<Recipient> groupMembers = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
memberSet.addAll(Stream.of(groupMembers).map(Recipient::getAddress).toList());
} else {
memberSet.addAll(Stream.of(destinations).map(GroupReceiptInfo::getAddress).toList());
}
// Replace primary device public key with ours so message syncing works correctly
String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
String localNumber = TextSecurePreferences.getLocalNumber(context);
if (masterHexEncodedPublicKey != null && memberSet.contains(Address.fromSerialized(masterHexEncodedPublicKey))) {
memberSet.remove(Address.fromSerialized(masterHexEncodedPublicKey));
memberSet.add(Address.fromSerialized(localNumber));
}
// Add secondary devices to the list. We shouldn't add our secondary devices
try {
Set<Address> originalMemberSet = new HashSet<>(memberSet);
for (Address member : originalMemberSet) {
if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; }
try {
Set<String> secondaryDevices = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(member.serialize()), 5000).get();
memberSet.addAll(Stream.of(secondaryDevices).map(string -> {
// Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :(
return Address.fromSerialized(string);
}).toList());
} catch (Exception e) {
// Timed out, go to the next member
}
}
} catch (Exception e) {
Log.e("PushGroupSend", "Error occurred while adding secondary devices: " + e);
}
return new LinkedList<>(memberSet);
}
}
public static class Factory implements Job.Factory<PushGroupSendJob> { public static class Factory implements Job.Factory<PushGroupSendJob> {
@Override @Override
public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) {

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