Merge branch 'dev' of https://github.com/loki-project/session-android into polling-limit-after-inactivity

This commit is contained in:
Brice-W 2021-05-24 13:00:02 +10:00
commit 1b25de6a96
81 changed files with 936 additions and 1644 deletions

View File

@ -1,10 +0,0 @@
[main]
host = https://www.transifex.com
lang_map = da_DK:da-rDK,he:iw,id:in,kn_IN:kn-rIN,pt_BR:pt-rBR,pt_PT:pt,qu_EC:qu-rEC,sv_SE:sv-rSE,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW
[signal-android.master]
file_filter = app/src/main/res/values-<lang>/strings.xml
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID

View File

@ -20,6 +20,39 @@ Please search for any [existing issues](https://github.com/oxen-io/session-andro
Build instructions can be found in [BUILDING.md](BUILDING.md). Build instructions can be found in [BUILDING.md](BUILDING.md).
## Verifing signatures
Get Kee's key and import it:
```
wget https://raw.githubusercontent.com/oxen-io/oxen-core/master/utils/gpg_keys/KeeJef.asc
gpg --import KeeJef.asc
```
Get the signed hash for this release, the SESSION_VERSION needs to be updated for the release you want to verify
```
export SESSION_VERSION=1.10.4
wget https://github.com/oxen-io/session-android/releases/download/$SESSION_VERSION/signatures.asc
```
Verify the signature of the hashes of the files
```
gpg --verify signatures.asc 2>&1 |grep "Good signature from"
```
The command above should print "`Good signature from "Kee Jefferys...`"
If it does, the hashes are valid but we still have to make the sure the signed hashes matches the downloaded files.
Make sure the two commands below returns the same hash.
If they do, files are valid.
```
sha256sum session-$SESSION_VERSION-universal.apk
grep universal.apk signatures.asc
```
## License ## License
Copyright 2011 Whisper Systems Copyright 2011 Whisper Systems

View File

@ -172,9 +172,6 @@
<activity <activity
android:name="org.thoughtcrime.securesms.loki.activities.ChatSettingsActivity" android:name="org.thoughtcrime.securesms.loki.activities.ChatSettingsActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity
android:name="org.thoughtcrime.securesms.loki.activities.LinkedDevicesActivity"
android:screenOrientation="portrait" />
<!-- Session --> <!-- Session -->
<activity <activity
android:name="org.thoughtcrime.securesms.ShareActivity" android:name="org.thoughtcrime.securesms.ShareActivity"
@ -224,24 +221,6 @@
android:resource="@mipmap/ic_launcher" /> android:resource="@mipmap/ic_launcher" />
</activity-alias> </activity-alias>
<activity
android:name="org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:launchMode="singleTask"
android:noHistory="true"
android:theme="@style/Theme.TextSecure.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="addstickers"
android:scheme="sgnl" />
</intent-filter>
</activity>
<activity <activity
android:name="org.thoughtcrime.securesms.conversation.ConversationActivity" android:name="org.thoughtcrime.securesms.conversation.ConversationActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
@ -282,23 +261,11 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar" /> android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar" />
<activity
android:name="org.thoughtcrime.securesms.PassphraseCreateActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:label="@string/AndroidManifest__create_passphrase"
android:launchMode="singleTask"
android:theme="@style/Theme.Session.DayNight.NoActionBar"
android:windowSoftInputMode="stateUnchanged" />
<activity <activity
android:name="org.thoughtcrime.securesms.PassphrasePromptActivity" android:name="org.thoughtcrime.securesms.PassphrasePromptActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/Theme.Session.DayNight.NoActionBar"/> android:theme="@style/Theme.Session.DayNight.NoActionBar"/>
<activity
android:name="org.thoughtcrime.securesms.PushContactSelectionActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:label="@string/AndroidManifest__select_contacts"
android:windowSoftInputMode="stateHidden" />
<activity <activity
android:name="org.thoughtcrime.securesms.giph.ui.GiphyActivity" android:name="org.thoughtcrime.securesms.giph.ui.GiphyActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"

View File

@ -30,26 +30,25 @@ import androidx.multidex.MultiDexApplication;
import org.conscrypt.Conscrypt; import org.conscrypt.Conscrypt;
import org.session.libsession.avatars.AvatarHelper; import org.session.libsession.avatars.AvatarHelper;
import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.messaging.MessagingModuleConfiguration;
import org.session.libsession.messaging.mentions.MentionsManager; import org.session.libsession.messaging.contacts.Contact;
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier; import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2; import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2;
import org.session.libsession.messaging.sending_receiving.pollers.Poller; import org.session.libsession.messaging.sending_receiving.pollers.Poller;
import org.session.libsession.snode.SnodeModule; import org.session.libsession.snode.SnodeModule;
import org.session.libsession.utilities.Address; import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.IdentityKeyUtil;
import org.session.libsession.utilities.ProfileKeyUtil;
import org.session.libsession.utilities.ProfilePictureUtilities; import org.session.libsession.utilities.ProfilePictureUtilities;
import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.SSKEnvironment;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper; import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
import org.session.libsession.utilities.dynamiclanguage.LocaleParser; import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
import org.session.libsignal.database.LokiAPIDatabaseProtocol; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
import org.session.libsignal.utilities.ThreadUtils; import org.session.libsignal.utilities.ThreadUtils;
import org.signal.aesgcmprovider.AesGcmProvider; import org.signal.aesgcmprovider.AesGcmProvider;
import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
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.jobmanager.DependencyInjector; import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
@ -65,9 +64,10 @@ import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager; import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
import org.thoughtcrime.securesms.loki.api.OpenGroupManager; import org.thoughtcrime.securesms.loki.api.OpenGroupManager;
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster; import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities;
import org.thoughtcrime.securesms.loki.utilities.FcmUtils; import org.thoughtcrime.securesms.loki.utilities.FcmUtils;
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities;
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
@ -85,14 +85,12 @@ import org.webrtc.PeerConnectionFactory;
import org.webrtc.PeerConnectionFactory.InitializationOptions; import org.webrtc.PeerConnectionFactory.InitializationOptions;
import org.webrtc.voiceengine.WebRtcAudioManager; import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioUtils; import org.webrtc.voiceengine.WebRtcAudioUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.security.Security; import java.security.Security;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import dagger.ObjectGraph; import dagger.ObjectGraph;
import kotlin.Unit; import kotlin.Unit;
import kotlinx.coroutines.Job; import kotlinx.coroutines.Job;
@ -153,30 +151,21 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
initializeDependencyInjection(); initializeDependencyInjection();
NotificationChannels.create(this); NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this); ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
// Loki
// ========
AppContext.INSTANCE.configureKovenant(); AppContext.INSTANCE.configureKovenant();
messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier());
broadcaster = new Broadcaster(this); broadcaster = new Broadcaster(this);
threadNotificationHandler = new Handler(Looper.getMainLooper()); threadNotificationHandler = new Handler(Looper.getMainLooper());
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
String userPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
MessagingModuleConfiguration.Companion.configure(this, MessagingModuleConfiguration.Companion.configure(this,
DatabaseFactory.getStorage(this), DatabaseFactory.getStorage(this),
DatabaseFactory.getAttachmentProvider(this)); DatabaseFactory.getAttachmentProvider(this));
SnodeModule.Companion.configure(apiDB, broadcaster); SnodeModule.Companion.configure(apiDB, broadcaster);
if (userPublicKey != null) {
MentionsManager.Companion.configureIfNeeded(userPublicKey, userDB);
}
resubmitProfilePictureIfNeeded(); resubmitProfilePictureIfNeeded();
if (userPublicKey != null) { if (userPublicKey != null) {
registerForFCMIfNeeded(false); registerForFCMIfNeeded(false);
} }
// Set application UI mode (day/night theme) to the user selected one.
UiModeUtilities.setupUiModeToUserSelected(this); UiModeUtilities.setupUiModeToUserSelected(this);
// ========
initializeExpiringMessageManager(); initializeExpiringMessageManager();
initializeTypingStatusRepository(); initializeTypingStatusRepository();
initializeTypingStatusSender(); initializeTypingStatusSender();
@ -194,12 +183,33 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
isAppVisible = true; isAppVisible = true;
Log.i(TAG, "App is now visible."); Log.i(TAG, "App is now visible.");
KeyCachingService.onAppForegrounded(this); KeyCachingService.onAppForegrounded(this);
// Loki
boolean hasPerformedContactMigration = TextSecurePreferences.INSTANCE.hasPerformedContactMigration(this);
if (!hasPerformedContactMigration) {
TextSecurePreferences.INSTANCE.setPerformedContactMigration(this);
Set<Recipient> allContacts = ContactUtilities.getAllContacts(this);
SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(this);
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
for (Recipient recipient : allContacts) {
if (recipient.isGroupRecipient()) { continue; }
String sessionID = recipient.getAddress().serialize();
Contact contact = contactDB.getContactWithSessionID(sessionID);
if (contact == null) {
contact = new Contact(sessionID);
String name = userDB.getDisplayName(sessionID);
contact.setName(name);
contact.setProfilePictureURL(recipient.getProfileAvatar());
contact.setProfilePictureEncryptionKey(recipient.getProfileKey());
contact.setTrusted(true);
}
contactDB.setContact(contact);
}
}
if (poller != null) { if (poller != null) {
poller.setCaughtUp(false); poller.setCaughtUp(false);
} }
startPollingIfNeeded(); startPollingIfNeeded();
OpenGroupManager.INSTANCE.setAllCaughtUp(false); OpenGroupManager.INSTANCE.setAllCaughtUp(false);
OpenGroupManager.INSTANCE.startPolling(); OpenGroupManager.INSTANCE.startPolling();
} }
@ -210,7 +220,6 @@ 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
if (poller != null) { if (poller != null) {
poller.stopIfNeeded(); poller.stopIfNeeded();
} }

View File

@ -18,8 +18,11 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.session.libsession.messaging.contacts.Contact;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
@ -197,7 +200,14 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener
if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) { if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) {
quoteeDisplayName = TextSecurePreferences.getProfileName(getContext()); quoteeDisplayName = TextSecurePreferences.getProfileName(getContext());
} else { } else {
quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(senderHexEncodedPublicKey); SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(getContext());
Contact contact = contactDB.getContactWithSessionID(senderHexEncodedPublicKey);
if (contact != null) {
Contact.ContactContext context = (this.conversationRecipient.isOpenGroupRecipient()) ? Contact.ContactContext.OPEN_GROUP : Contact.ContactContext.REGULAR;
quoteeDisplayName = contact.displayName(context);
} else {
quoteeDisplayName = senderHexEncodedPublicKey;
}
} }
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you) : quoteeDisplayName); authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you) : quoteeDisplayName);

View File

@ -62,7 +62,6 @@ import android.widget.ImageView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -103,6 +102,7 @@ import org.session.libsession.utilities.recipients.RecipientModifiedListener;
import org.session.libsession.utilities.ExpirationUtil; import org.session.libsession.utilities.ExpirationUtil;
import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.MediaTypes; import org.session.libsession.utilities.MediaTypes;
import org.session.libsession.utilities.SSKEnvironment;
import org.session.libsession.utilities.ServiceUtil; import org.session.libsession.utilities.ServiceUtil;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
@ -157,8 +157,6 @@ import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity; import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity;
import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker; import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
@ -212,14 +210,14 @@ import network.loki.messenger.R;
*/ */
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
public class ConversationActivity extends PassphraseRequiredActionBarActivity public class ConversationActivity extends PassphraseRequiredActionBarActivity
implements ConversationFragment.ConversationFragmentListener, implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener, AttachmentManager.AttachmentListener,
RecipientModifiedListener, RecipientModifiedListener,
OnKeyboardShownListener, OnKeyboardShownListener,
InputPanel.Listener, InputPanel.Listener,
InputPanel.MediaListener, InputPanel.MediaListener,
ComposeText.CursorPositionChangedListener, ComposeText.CursorPositionChangedListener,
ConversationSearchBottomBar.EventListener ConversationSearchBottomBar.EventListener
{ {
private static final String TAG = ConversationActivity.class.getSimpleName(); private static final String TAG = ConversationActivity.class.getSimpleName();
@ -234,11 +232,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public static final String LAST_SEEN_EXTRA = "last_seen"; public static final String LAST_SEEN_EXTRA = "last_seen";
public static final String STARTING_POSITION_EXTRA = "starting_position"; public static final String STARTING_POSITION_EXTRA = "starting_position";
// private static final int PICK_GALLERY = 1; // private static final int PICK_GALLERY = 1;
private static final int PICK_DOCUMENT = 2; private static final int PICK_DOCUMENT = 2;
private static final int PICK_AUDIO = 3; private static final int PICK_AUDIO = 3;
private static final int PICK_CONTACT = 4; private static final int PICK_CONTACT = 4;
// private static final int GET_CONTACT_DETAILS = 5; // private static final int GET_CONTACT_DETAILS = 5;
// private static final int GROUP_EDIT = 6; // private static final int GROUP_EDIT = 6;
private static final int TAKE_PHOTO = 7; private static final int TAKE_PHOTO = 7;
private static final int ADD_CONTACT = 8; private static final int ADD_CONTACT = 8;
@ -513,90 +511,89 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
super.onActivityResult(reqCode, resultCode, data); super.onActivityResult(reqCode, resultCode, data);
if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) || if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) ||
(resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) (resultCode != RESULT_OK && reqCode != SMS_DEFAULT))
{ {
updateLinkPreviewState(); updateLinkPreviewState();
return; return;
} }
switch (reqCode) { switch (reqCode) {
case PICK_DOCUMENT: case PICK_DOCUMENT:
setMedia(data.getData(), MediaType.DOCUMENT); setMedia(data.getData(), MediaType.DOCUMENT);
break; break;
case PICK_AUDIO: case PICK_AUDIO:
setMedia(data.getData(), MediaType.AUDIO); setMedia(data.getData(), MediaType.AUDIO);
break; break;
case TAKE_PHOTO: case TAKE_PHOTO:
if (attachmentManager.getCaptureUri() != null) { if (attachmentManager.getCaptureUri() != null) {
setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE);
} }
break; break;
case ADD_CONTACT: case ADD_CONTACT:
recipient = Recipient.from(this, recipient.getAddress(), true); recipient = Recipient.from(this, recipient.getAddress(), true);
recipient.addListener(this); recipient.addListener(this);
fragment.reloadList(); fragment.reloadList();
break; break;
/* /*
case PICK_LOCATION: case PICK_LOCATION:
SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
attachmentManager.setLocation(place, getCurrentMediaConstraints()); attachmentManager.setLocation(place, getCurrentMediaConstraints());
break; break;
*/ */
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(true, isDefaultSms); initializeSecurity(true, isDefaultSms);
break; break;
case MEDIA_SENDER: case MEDIA_SENDER:
long expiresIn = recipient.getExpireMessages() * 1000L; long expiresIn = recipient.getExpireMessages() * 1000L;
int subscriptionId = -1; int subscriptionId = -1;
boolean initiating = threadId == -1; boolean initiating = threadId == -1;
String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE);
SlideDeck slideDeck = new SlideDeck(); SlideDeck slideDeck = new SlideDeck();
List<Media> mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); List<Media> mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA);
for (Media mediaItem : mediaList) { for (Media mediaItem : mediaList) {
if (MediaUtil.isVideoType(mediaItem.getMimeType())) { if (MediaUtil.isVideoType(mediaItem.getMimeType())) {
slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull()));
} else if (MediaUtil.isGif(mediaItem.getMimeType())) { } else if (MediaUtil.isGif(mediaItem.getMimeType())) {
slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull()));
} else if (MediaUtil.isImageType(mediaItem.getMimeType())) { } else if (MediaUtil.isImageType(mediaItem.getMimeType())) {
slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull()));
} else { } else {
Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping.");
}
} }
}
final Context context = ConversationActivity.this.getApplicationContext(); final Context context = ConversationActivity.this.getApplicationContext();
sendMediaMessage(message, sendMediaMessage(message,
slideDeck, slideDeck,
inputPanel.getQuote().orNull(), inputPanel.getQuote().orNull(),
Optional.absent(), Optional.absent(),
initiating).addListener(new AssertedSuccessListener<Void>() { initiating).addListener(new AssertedSuccessListener<Void>() {
@Override @Override
public void onSuccess(Void result) { public void onSuccess(Void result) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
Stream.of(slideDeck.getSlides()) Stream.of(slideDeck.getSlides())
.map(Slide::getUri) .map(Slide::getUri)
.withoutNulls() .withoutNulls()
.filter(BlobProvider::isAuthority) .filter(BlobProvider::isAuthority)
.forEach(uri -> BlobProvider.getInstance().delete(context, uri)); .forEach(uri -> BlobProvider.getInstance().delete(context, uri));
}); });
} }
}); });
break;
break; case INVITE_CONTACTS:
case INVITE_CONTACTS: if (data.getExtras() == null || !data.hasExtra(SelectContactsActivity.Companion.getSelectedContactsKey())) return;
if (data.getExtras() == null || !data.hasExtra(SelectContactsActivity.Companion.getSelectedContactsKey())) return; String[] selectedContacts = data.getExtras().getStringArray(SelectContactsActivity.Companion.getSelectedContactsKey());
String[] selectedContacts = data.getExtras().getStringArray(SelectContactsActivity.Companion.getSelectedContactsKey()); sendOpenGroupInvitations(selectedContacts);
sendOpenGroupInvitations(selectedContacts); break;
break;
} }
} }
@ -676,14 +673,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (isSingleConversation() && getRecipient().getContactUri() == null) { if (isSingleConversation() && getRecipient().getContactUri() == null) {
inflater.inflate(R.menu.conversation_add_to_contacts, menu); inflater.inflate(R.menu.conversation_add_to_contacts, menu);
} }
if (recipient != null && recipient.isLocalNumber()) { if (recipient != null && recipient.isLocalNumber()) {
if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false); if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false);
else menu.findItem(R.id.menu_call_insecure).setVisible(false); else menu.findItem(R.id.menu_call_insecure).setVisible(false);
MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications); MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications);
if (muteItem != null) { if (muteItem != null) {
muteItem.setVisible(false); muteItem.setVisible(false);
} }
@ -751,26 +744,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
switch (item.getItemId()) { switch (item.getItemId()) {
// case R.id.menu_call_secure: handleDial(getRecipient(), true); return true; // case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
// case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; // case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
case R.id.menu_unblock: handleUnblock(); return true; case R.id.menu_unblock: handleUnblock(); return true;
case R.id.menu_block: handleBlock(); return true; case R.id.menu_block: handleBlock(); return true;
case R.id.menu_copy_session_id: handleCopySessionID(); return true; case R.id.menu_copy_session_id: handleCopySessionID(); return true;
case R.id.menu_view_media: handleViewMedia(); return true; case R.id.menu_view_media: handleViewMedia(); return true;
case R.id.menu_add_shortcut: handleAddShortcut(); return true; case R.id.menu_add_shortcut: handleAddShortcut(); return true;
case R.id.menu_search: handleSearch(); return true; case R.id.menu_search: handleSearch(); return true;
// case R.id.menu_add_to_contacts: handleAddToContacts(); return true; // case R.id.menu_add_to_contacts: handleAddToContacts(); return true;
// case R.id.menu_reset_secure_session: handleResetSecureSession(); return true; // case R.id.menu_reset_secure_session: handleResetSecureSession(); return true;
// case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true; // case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true; case R.id.menu_distribution_broadcast: handleDistributionBroadcastEnabled(item); return true;
case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true; case R.id.menu_distribution_conversation: handleDistributionConversationEnabled(item); return true;
case R.id.menu_edit_group: handleEditPushGroup(); return true; case R.id.menu_edit_group: handleEditPushGroup(); return true;
case R.id.menu_leave: handleLeavePushGroup(); return true; case R.id.menu_leave: handleLeavePushGroup(); 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 R.id.menu_invite_to_open_group: handleInviteToOpenGroup(); return true; case R.id.menu_invite_to_open_group: handleInviteToOpenGroup(); return true;
case android.R.id.home: handleReturnToConversationList(); return true; case android.R.id.home: handleReturnToConversationList(); return true;
} }
return false; return false;
@ -841,7 +834,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this) DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setMuted(recipient, until); .setMuted(recipient, until);
return null; return null;
} }
@ -856,7 +849,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this) DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setMuted(recipient, 0); .setMuted(recipient, 0);
return null; return null;
} }
@ -868,20 +861,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact; int bodyRes = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact;
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(titleRes) .setTitle(titleRes)
.setMessage(bodyRes) .setMessage(bodyRes)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> {
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this) DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setBlocked(recipient, false); .setBlocked(recipient, false);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}).show(); }).show();
} }
@TargetApi(Build.VERSION_CODES.KITKAT) @TargetApi(Build.VERSION_CODES.KITKAT)
@ -951,7 +944,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (icon == null) { if (icon == null) {
icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut
: R.mipmap.ic_person_shortcut); : R.mipmap.ic_person_shortcut);
} }
return icon; return icon;
@ -961,14 +954,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected void onPostExecute(IconCompat icon) { protected void onPostExecute(IconCompat icon) {
Context context = getApplicationContext(); Context context = getApplicationContext();
String name = Optional.fromNullable(recipient.getName()) String name = Optional.fromNullable(recipient.getName())
.or(Optional.fromNullable(recipient.getProfileName())) .or(Optional.fromNullable(recipient.getProfileName()))
.or(recipient.toShortString()); .or(recipient.toShortString());
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis()) ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis())
.setShortLabel(name) .setShortLabel(name)
.setIcon(icon) .setIcon(icon)
.setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress()))
.build(); .build();
if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) { if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show(); Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show();
@ -984,7 +977,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleLeavePushGroup() { private void handleLeavePushGroup() {
if (getRecipient() == null) { if (getRecipient() == null) {
Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient), Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
return; return;
} }
@ -1052,7 +1045,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this) DatabaseFactory.getThreadDatabase(ConversationActivity.this)
.setDistributionType(threadId, DistributionTypes.BROADCAST); .setDistributionType(threadId, DistributionTypes.BROADCAST);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1068,7 +1061,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this) DatabaseFactory.getThreadDatabase(ConversationActivity.this)
.setDistributionType(threadId, DistributionTypes.CONVERSATION); .setDistributionType(threadId, DistributionTypes.CONVERSATION);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1100,7 +1093,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
/* Loki - We don't support SMS /* Loki - We don't support SMS
if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE); if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE);
if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS); if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS);
if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) { if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) {
sendButton.setDefaultTransport(Type.SMS); sendButton.setDefaultTransport(Type.SMS);
} else { } else {
@ -1425,35 +1417,35 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.i(TAG, "Selected: " + type); Log.i(TAG, "Selected: " + type);
switch (type) { switch (type) {
case AttachmentTypeSelector.ADD_GALLERY: case AttachmentTypeSelector.ADD_GALLERY:
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break; AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break;
case AttachmentTypeSelector.ADD_DOCUMENT: case AttachmentTypeSelector.ADD_DOCUMENT:
AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; AttachmentManager.selectDocument(this, PICK_DOCUMENT); break;
case AttachmentTypeSelector.ADD_SOUND: case AttachmentTypeSelector.ADD_SOUND:
AttachmentManager.selectAudio(this, PICK_AUDIO); break; AttachmentManager.selectAudio(this, PICK_AUDIO); break;
case AttachmentTypeSelector.ADD_CONTACT_INFO: case AttachmentTypeSelector.ADD_CONTACT_INFO:
AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; AttachmentManager.selectContactInfo(this, PICK_CONTACT); break;
case AttachmentTypeSelector.ADD_LOCATION: case AttachmentTypeSelector.ADD_LOCATION:
AttachmentManager.selectLocation(this, PICK_LOCATION); break; AttachmentManager.selectLocation(this, PICK_LOCATION); break;
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:
boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this);
if (!hasSeenGIFMetaDataWarning) { if (!hasSeenGIFMetaDataWarning) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Search GIFs?"); builder.setTitle("Search GIFs?");
builder.setMessage("You will not have full metadata protection when sending GIFs."); builder.setMessage("You will not have full metadata protection when sending GIFs.");
builder.setPositiveButton("OK", (dialog, which) -> { builder.setPositiveButton("OK", (dialog, which) -> {
AttachmentManager.selectGif(this, PICK_GIF);
dialog.dismiss();
});
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss());
builder.create().show();
TextSecurePreferences.setHasSeenGIFMetaDataWarning(this);
} else {
AttachmentManager.selectGif(this, PICK_GIF); AttachmentManager.selectGif(this, PICK_GIF);
dialog.dismiss(); }
}); break;
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss());
builder.create().show();
TextSecurePreferences.setHasSeenGIFMetaDataWarning(this);
} else {
AttachmentManager.selectGif(this, PICK_GIF);
}
break;
} }
} }
@ -1548,8 +1540,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
draftDatabase.insertDrafts(threadId, drafts); draftDatabase.insertDrafts(threadId, drafts);
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
drafts.getUriSnippet(), drafts.getUriSnippet(),
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
} else if (threadId > 0) { } else if (threadId > 0) {
threadDatabase.update(threadId, false); threadDatabase.update(threadId, false);
} }
@ -1652,10 +1644,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date()); String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date());
String filename = String.format("signal-%s.txt", timestamp); String filename = String.format("signal-%s.txt", timestamp);
Uri textUri = BlobProvider.getInstance() Uri textUri = BlobProvider.getInstance()
.forData(textData) .forData(textData)
.withMimeType(MediaTypes.LONG_TEXT) .withMimeType(MediaTypes.LONG_TEXT)
.withFileName(filename) .withFileName(filename)
.createForSingleSessionInMemory(); .createForSingleSessionInMemory();
textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length)); textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length));
} }
@ -1733,10 +1725,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize; boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize;
boolean isMediaMessage = attachmentManager.isAttachmentPresent() || boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
// recipient.isGroupRecipient() || // recipient.isGroupRecipient() ||
inputPanel.getQuote().isPresent() || inputPanel.getQuote().isPresent() ||
linkPreviewViewModel.hasLinkPreview() || linkPreviewViewModel.hasLinkPreview() ||
LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages
needsSplit; needsSplit;
if (isMediaMessage) { if (isMediaMessage) {
sendMediaMessage(initiating); sendMediaMessage(initiating);
@ -1757,7 +1749,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void sendMediaMessage(boolean initiating) private void sendMediaMessage(boolean initiating)
throws InvalidMessageException throws InvalidMessageException
{ {
Log.i(TAG, "Sending media message..."); Log.i(TAG, "Sending media message...");
sendMediaMessage(getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), linkPreviewViewModel.getActiveLinkPreview(), initiating); sendMediaMessage(getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), linkPreviewViewModel.getActiveLinkPreview(), initiating);
@ -1817,7 +1809,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void sendTextMessage(final boolean initiating) private void sendTextMessage(final boolean initiating)
throws InvalidMessageException throws InvalidMessageException
{ {
final Context context = getApplicationContext(); final Context context = getApplicationContext();
final String messageBody = getMessage(); final String messageBody = getMessage();
@ -1895,10 +1887,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onRecorderPermissionRequired() { public void onRecorderPermissionRequired() {
Permissions.with(this) Permissions.with(this)
.request(Manifest.permission.RECORD_AUDIO) .request(Manifest.permission.RECORD_AUDIO)
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48) .withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
.execute(); .execute();
} }
@Override @Override
@ -2057,16 +2049,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Permissions.with(ConversationActivity.this) Permissions.with(ConversationActivity.this)
.request(Manifest.permission.CAMERA) .request(Manifest.permission.CAMERA)
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48) .withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_baseline_photo_camera_48)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
.onAllGranted(() -> { .onAllGranted(() -> {
composeText.clearFocus(); composeText.clearFocus();
startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER); startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER);
overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary); overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary);
}) })
.onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show()) .onAnyDenied(() -> Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
.execute(); .execute();
} }
} }
@ -2176,7 +2168,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
if (text.length() > 0) { if (text.length() > 0) {
if (currentMentionStartIndex > text.length()) { if (currentMentionStartIndex > text.length()) {
resetMentions(); // Should never occur resetMentions(); // Should never occur
} }
int lastCharacterIndex = text.length() - 1; int lastCharacterIndex = text.length() - 1;
char lastCharacter = text.charAt(lastCharacterIndex); char lastCharacter = text.charAt(lastCharacterIndex);
@ -2184,11 +2176,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (lastCharacterIndex > 0) { if (lastCharacterIndex > 0) {
secondToLastCharacter = text.charAt(lastCharacterIndex - 1); secondToLastCharacter = text.charAt(lastCharacterIndex - 1);
} }
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(ConversationActivity.this);
LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this);
if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) { if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) {
List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId); List<Mention> mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates("", threadId, recipient.isOpenGroupRecipient());
currentMentionStartIndex = lastCharacterIndex; currentMentionStartIndex = lastCharacterIndex;
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId); mentionCandidateSelectionView.show(mentionCandidates, threadId);
@ -2199,7 +2188,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 = MentionsManager.shared.getMentionCandidates(query, threadId); List<Mention> mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates(query, threadId, recipient.isOpenGroupRecipient());
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId); mentionCandidateSelectionView.show(mentionCandidates, threadId);
} }
@ -2243,12 +2232,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);
@ -2259,26 +2248,26 @@ 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);
} }
} }
@Override @Override
public void onMessageActionToolbarOpened() { public void onMessageActionToolbarOpened() {
searchViewItem.collapseActionView(); searchViewItem.collapseActionView();
} }
@Override @Override
@ -2340,12 +2329,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void updateTitleTextView(Recipient recipient) { private void updateTitleTextView(Recipient recipient) {
String userPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
if (recipient == null) { if (recipient == null) {
titleTextView.setText("Compose"); titleTextView.setText(R.string.ConversationActivity_compose);
} else if (recipient.getAddress().toString().toLowerCase().equals(userPublicKey)) { } else if (recipient.getAddress().toString().toLowerCase().equals(userPublicKey)) {
titleTextView.setText(getResources().getString(R.string.note_to_self)); titleTextView.setText(R.string.note_to_self);
} else { } else {
boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); String displayName = recipient.getName(); // Uses the Contact API internally
titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString()); boolean hasName = (displayName != null);
titleTextView.setText(hasName ? displayName : recipient.getAddress().toString());
} }
} }
@ -2363,13 +2353,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
subtitleTextView.setVisibility(View.VISIBLE); subtitleTextView.setVisibility(View.VISIBLE);
if (recipient.isMuted()) { if (recipient.isMuted()) {
muteIndicatorImageView.setVisibility(View.VISIBLE); muteIndicatorImageView.setVisibility(View.VISIBLE);
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())); subtitleTextView.setText(getString(R.string.ConversationActivity_muted_until_date,DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())));
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) { } else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId); OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId);
if (openGroup != null) { if (openGroup != null) {
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(openGroup.getRoom(),openGroup.getServer()); Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(openGroup.getRoom(),openGroup.getServer());
if (userCount == null) { userCount = 0; } if (userCount == null) { userCount = 0; }
subtitleTextView.setText(userCount + " members"); subtitleTextView.setText(getString(R.string.ConversationActivity_member_count,userCount));
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) { } else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
subtitleTextView.setText(recipient.getAddress().toString()); subtitleTextView.setText(recipient.getAddress().toString());
} else { } else {
@ -2391,7 +2381,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void updateMessageStatusProgressBar() { private void updateMessageStatusProgressBar() {
if (messageStatus != null) { if (messageStatus != null) {
messageStatusProgressBar.setAlpha(1.0f); messageStatusProgressBar.setAlpha(1.0f);
switch (messageStatus) { switch (messageStatus) {
case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break; case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break;
case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break; case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break;
@ -2428,7 +2418,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
default: return -1; default: return -1;
} }
} else { } else {
return -1; return -1;
} }
} }

View File

@ -49,6 +49,8 @@ import androidx.annotation.DimenRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.session.libsession.messaging.contacts.Contact;
import org.session.libsession.messaging.jobs.AttachmentDownloadJob; import org.session.libsession.messaging.jobs.AttachmentDownloadJob;
import org.session.libsession.messaging.jobs.JobQueue; import org.session.libsession.messaging.jobs.JobQueue;
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2; import org.session.libsession.messaging.open_groups.OpenGroupAPIV2;
@ -889,7 +891,15 @@ public class ConversationItem extends LinearLayout
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) { private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) {
if (groupThread && !messageRecord.isOutgoing()) { if (groupThread && !messageRecord.isOutgoing()) {
String displayName = recipient.toShortString(); String sessionID = recipient.getAddress().serialize();
Contact contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(sessionID);
String displayName;
if (contact != null) {
Contact.ContactContext context = (this.conversationRecipient.isOpenGroupRecipient()) ? Contact.ContactContext.OPEN_GROUP : Contact.ContactContext.REGULAR;
displayName = contact.displayName(context);
} else {
displayName = sessionID;
}
this.groupSender.setText(displayName); this.groupSender.setText(displayName);

View File

@ -17,11 +17,8 @@
package org.thoughtcrime.securesms.database; package org.thoughtcrime.securesms.database;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider; import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider;
import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
@ -34,6 +31,7 @@ import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SessionJobDatabase; import org.thoughtcrime.securesms.loki.database.SessionJobDatabase;
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
public class DatabaseFactory { public class DatabaseFactory {
@ -55,16 +53,13 @@ public class DatabaseFactory {
private final GroupReceiptDatabase groupReceiptDatabase; private final GroupReceiptDatabase groupReceiptDatabase;
private final SearchDatabase searchDatabase; private final SearchDatabase searchDatabase;
private final JobDatabase jobDatabase; private final JobDatabase jobDatabase;
// Loki
private final LokiAPIDatabase lokiAPIDatabase; private final LokiAPIDatabase lokiAPIDatabase;
private final LokiMessageDatabase lokiMessageDatabase; private final LokiMessageDatabase lokiMessageDatabase;
private final LokiThreadDatabase lokiThreadDatabase; private final LokiThreadDatabase lokiThreadDatabase;
private final LokiUserDatabase lokiUserDatabase; private final LokiUserDatabase lokiUserDatabase;
private final LokiBackupFilesDatabase lokiBackupFilesDatabase; private final LokiBackupFilesDatabase lokiBackupFilesDatabase;
private final SessionJobDatabase sessionJobDatabase; private final SessionJobDatabase sessionJobDatabase;
private final SessionContactDatabase sessionContactDatabase;
// Refactor
private final Storage storage; private final Storage storage;
private final DatabaseAttachmentProvider attachmentProvider; private final DatabaseAttachmentProvider attachmentProvider;
@ -157,6 +152,10 @@ public class DatabaseFactory {
public static SessionJobDatabase getSessionJobDatabase(Context context) { public static SessionJobDatabase getSessionJobDatabase(Context context) {
return getInstance(context).sessionJobDatabase; return getInstance(context).sessionJobDatabase;
} }
public static SessionContactDatabase getSessionContactDatabase(Context context) {
return getInstance(context).sessionContactDatabase;
}
// endregion // endregion
// region Refactor // region Refactor
@ -202,6 +201,7 @@ public class DatabaseFactory {
this.storage = new Storage(context, databaseHelper); this.storage = new Storage(context, databaseHelper);
this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper); this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper);
this.sessionJobDatabase = new SessionJobDatabase(context, databaseHelper); this.sessionJobDatabase = new SessionJobDatabase(context, databaseHelper);
this.sessionContactDatabase = new SessionContactDatabase(context, databaseHelper);
} }
} }

View File

@ -248,6 +248,7 @@ public class RecipientDatabase extends Database {
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(SYSTEM_DISPLAY_NAME, profileName); contentValues.put(SYSTEM_DISPLAY_NAME, profileName);
updateOrInsert(recipient.getAddress(), contentValues); updateOrInsert(recipient.getAddress(), contentValues);
recipient.resolve().setName(profileName);
recipient.resolve().setProfileName(profileName); recipient.resolve().setProfileName(profileName);
} }

View File

@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.database
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import org.session.libsession.database.StorageProtocol import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.AttachmentUploadJob
import org.session.libsession.messaging.jobs.Job import org.session.libsession.messaging.jobs.Job
import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.JobQueue
@ -80,23 +80,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, newValue)) ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, newValue))
} }
override fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray? {
val address = Address.fromSerialized(recipientPublicKey)
val recipient = Recipient.from(context, address, false)
return recipient.profileKey
}
override fun getDisplayNameForRecipient(recipientPublicKey: String): String? {
val database = DatabaseFactory.getLokiUserDatabase(context)
return database.getDisplayName(recipientPublicKey)
}
override fun setProfileKeyForRecipient(recipientPublicKey: String, profileKey: ByteArray) {
val address = Address.fromSerialized(recipientPublicKey)
val recipient = Recipient.from(context, address, false)
DatabaseFactory.getRecipientDatabase(context).setProfileKey(recipient, profileKey)
}
override fun getOrGenerateRegistrationID(): Int { override fun getOrGenerateRegistrationID(): Int {
var registrationID = TextSecurePreferences.getLocalRegistrationId(context) var registrationID = TextSecurePreferences.getLocalRegistrationId(context)
if (registrationID == 0) { if (registrationID == 0) {
@ -512,16 +495,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return threadId return threadId
} }
override fun getDisplayName(publicKey: String): String? { override fun getContactWithSessionID(sessionID: String): Contact? {
return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) return DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(sessionID)
} }
override fun setDisplayName(publicKey: String, newName: String) { override fun getAllContacts(): Set<Contact> {
DatabaseFactory.getLokiUserDatabase(context).setDisplayName(publicKey, newName) return DatabaseFactory.getSessionContactDatabase(context).getAllContacts()
} }
override fun getProfilePictureURL(publicKey: String): String? { override fun setContact(contact: Contact) {
return DatabaseFactory.getLokiUserDatabase(context).getProfilePictureURL(publicKey) DatabaseFactory.getSessionContactDatabase(context).setContact(contact)
} }
override fun getRecipientSettings(address: Address): Recipient.RecipientSettings? { override fun getRecipientSettings(address: Address): Recipient.RecipientSettings? {

View File

@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
import org.thoughtcrime.securesms.loki.database.SessionJobDatabase; import org.thoughtcrime.securesms.loki.database.SessionJobDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsMigration; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsMigration;
@ -57,9 +58,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV23 = 44; private static final int lokiV23 = 44;
private static final int lokiV24 = 45; private static final int lokiV24 = 45;
private static final int lokiV25 = 46; private static final int lokiV25 = 46;
private static final int lokiV26 = 47;
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final int DATABASE_VERSION = lokiV25; private static final int DATABASE_VERSION = lokiV26;
private static final String DATABASE_NAME = "signal.db"; private static final String DATABASE_NAME = "signal.db";
private final Context context; private final Context context;
@ -124,11 +126,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand()); db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand());
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand());
db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand());
db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand());
db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand());
db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType()); db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType());
db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable()); db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable());
db.execSQL(SessionContactDatabase.getCreateSessionContactTableCommand());
executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS);
@ -298,6 +300,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand());
} }
if (oldVersion < lokiV26) {
db.execSQL(SessionContactDatabase.getCreateSessionContactTableCommand());
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View File

@ -135,12 +135,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
}) })
// Set up remaining components if needed // Set up remaining components if needed
val application = ApplicationContext.getInstance(this) val application = ApplicationContext.getInstance(this)
val apiDB = DatabaseFactory.getLokiAPIDatabase(this)
val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
val userDB = DatabaseFactory.getLokiUserDatabase(this)
val userPublicKey = TextSecurePreferences.getLocalNumber(this) val userPublicKey = TextSecurePreferences.getLocalNumber(this)
if (userPublicKey != null) { if (userPublicKey != null) {
MentionsManager.configureIfNeeded(userPublicKey, userDB)
OpenGroupManager.startPolling() OpenGroupManager.startPolling()
JobQueue.shared.resumePendingJobs() JobQueue.shared.resumePendingJobs()
} }

View File

@ -72,7 +72,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_settings) setContentView(R.layout.activity_settings)
val displayName = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) val displayName = TextSecurePreferences.getProfileName(this) ?: hexEncodedPublicKey
glide = GlideApp.with(this) glide = GlideApp.with(this)
profilePictureView.glide = glide profilePictureView.glide = glide
profilePictureView.publicKey = hexEncodedPublicKey profilePictureView.publicKey = hexEncodedPublicKey

View File

@ -278,14 +278,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
} }
} }
override fun getLastMessageServerID(group: Long, server: String): Long? {
val database = databaseHelper.readableDatabase
val index = "$server.$group"
return database.get(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) { cursor ->
cursor.getInt(lastMessageServerID)
}?.toLong()
}
override fun getLastMessageServerID(room: String, server: String): Long? { override fun getLastMessageServerID(room: String, server: String): Long? {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val index = "$server.$room" val index = "$server.$room"
@ -294,13 +286,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}?.toLong() }?.toLong()
} }
override fun setLastMessageServerID(group: Long, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
val row = wrap(mapOf( lastMessageServerIDTableIndex to index, lastMessageServerID to newValue.toString() ))
database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index))
}
override fun setLastMessageServerID(room: String, server: String, newValue: Long) { override fun setLastMessageServerID(room: String, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val index = "$server.$room" val index = "$server.$room"
@ -308,26 +293,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index)) database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index))
} }
fun removeLastMessageServerID(group: Long, server: String) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
database.delete(lastMessageServerIDTable,"$lastMessageServerIDTableIndex = ?", wrap(index))
}
fun removeLastMessageServerID(room: String, server:String) { fun removeLastMessageServerID(room: String, server:String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val index = "$server.$room" val index = "$server.$room"
database.delete(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) database.delete(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index))
} }
override fun getLastDeletionServerID(group: Long, server: String): Long? {
val database = databaseHelper.readableDatabase
val index = "$server.$group"
return database.get(lastDeletionServerIDTable, "$lastDeletionServerIDTableIndex = ?", wrap(index)) { cursor ->
cursor.getInt(lastDeletionServerID)
}?.toLong()
}
override fun getLastDeletionServerID(room: String, server: String): Long? { override fun getLastDeletionServerID(room: String, server: String): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
val index = "$server.$room" val index = "$server.$room"
@ -336,13 +307,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}?.toLong() }?.toLong()
} }
override fun setLastDeletionServerID(group: Long, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
val row = wrap(mapOf( lastDeletionServerIDTableIndex to index, lastDeletionServerID to newValue.toString() ))
database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index))
}
override fun setLastDeletionServerID(room: String, server: String, newValue: Long) { override fun setLastDeletionServerID(room: String, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val index = "$server.$room" val index = "$server.$room"
@ -392,32 +356,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index)) database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index))
} }
override fun getSessionRequestSentTimestamp(publicKey: String): Long? {
val database = databaseHelper.readableDatabase
return database.get(sessionRequestSentTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->
cursor.getLong(LokiAPIDatabase.timestamp)
}?.toLong()
}
override fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val row = wrap(mapOf( LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString() ))
database.insertOrUpdate(sessionRequestSentTimestampTable, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey))
}
override fun getSessionRequestProcessedTimestamp(publicKey: String): Long? {
val database = databaseHelper.readableDatabase
return database.get(sessionRequestProcessedTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->
cursor.getInt(LokiAPIDatabase.timestamp)
}?.toLong()
}
override fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to newValue.toString()))
database.insertOrUpdate(sessionRequestProcessedTimestampTable, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey))
}
override fun getOpenGroupPublicKey(server: String): String? { override fun getOpenGroupPublicKey(server: String): String? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(openGroupPublicKeyTable, "${LokiAPIDatabase.server} = ?", wrap(server)) { cursor -> return database.get(openGroupPublicKeyTable, "${LokiAPIDatabase.server} = ?", wrap(server)) { cursor ->
@ -431,27 +369,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(openGroupPublicKeyTable, row, "${LokiAPIDatabase.server} = ?", wrap(server)) database.insertOrUpdate(openGroupPublicKeyTable, row, "${LokiAPIDatabase.server} = ?", wrap(server))
} }
override fun getOpenGroupProfilePictureURL(group: Long, server: String): String? {
val database = databaseHelper.readableDatabase
val index = "$server.$group"
return database.get(openGroupProfilePictureTable, "$publicChatID = ?", wrap(index)) { cursor ->
cursor.getString(openGroupProfilePicture)
}?.toString()
}
override fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
val row = wrap(mapOf(publicChatID to index, openGroupProfilePicture to newValue))
database.insertOrUpdate(openGroupProfilePictureTable, row, "$publicChatID = ?", wrap(index))
}
fun clearOpenGroupProfilePictureURL(group: Long, server: String): Boolean {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
return database.delete(openGroupProfilePictureTable, "$publicChatID = ?", arrayOf(index)) > 0
}
override fun getLastSnodePoolRefreshDate(): Date? { override fun getLastSnodePoolRefreshDate(): Date? {
val time = TextSecurePreferences.getLastSnodePoolRefreshDate(context) val time = TextSecurePreferences.getLastSnodePoolRefreshDate(context)
if (time <= 0) { return null } if (time <= 0) { return null }

View File

@ -37,11 +37,6 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
} }
override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? {
val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, quoteePublicKey)
return if (message != null) getServerID(message.getId(), !message.isMms) else null
}
fun getServerID(messageID: Long): Long? { fun getServerID(messageID: Long): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor -> return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor ->

View File

@ -1,19 +1,12 @@
package org.thoughtcrime.securesms.loki.database package org.thoughtcrime.securesms.loki.database
import android.content.ContentValues
import android.content.Context import android.content.Context
import android.database.sqlite.SQLiteDatabase
import org.session.libsignal.utilities.Log
import org.session.libsession.utilities.Address
import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.get import org.thoughtcrime.securesms.loki.utilities.get
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.database.LokiUserDatabaseProtocol
class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol { class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
companion object { companion object {
// Shared // Shared
@ -28,7 +21,7 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
@JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($publicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($publicKey, $serverID));" @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($publicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($publicKey, $serverID));"
} }
override fun getDisplayName(publicKey: String): String? { fun getDisplayName(publicKey: String): String? {
if (publicKey == TextSecurePreferences.getLocalNumber(context)) { if (publicKey == TextSecurePreferences.getLocalNumber(context)) {
return TextSecurePreferences.getProfileName(context) return TextSecurePreferences.getProfileName(context)
} else { } else {
@ -44,21 +37,4 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
} }
} }
} }
fun setDisplayName(publicKey: String, displayName: String) {
val database = databaseHelper.writableDatabase
val row = ContentValues(2)
row.put(Companion.publicKey, publicKey)
row.put(Companion.displayName, displayName)
database.insertOrUpdate(displayNameTable, row, "${Companion.publicKey} = ?", arrayOf( publicKey ))
Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners()
}
override fun getProfilePictureURL(publicKey: String): String? {
return if (publicKey == TextSecurePreferences.getLocalNumber(context)) {
TextSecurePreferences.getProfilePictureURL(context)
} else {
Recipient.from(context, Address.fromSerialized(publicKey), false).resolve().profileAvatar
}
}
} }

View File

@ -0,0 +1,81 @@
package org.thoughtcrime.securesms.loki.database
import android.content.ContentValues
import android.content.Context
import net.sqlcipher.Cursor
import org.session.libsession.messaging.contacts.Contact
import org.session.libsignal.utilities.Base64
import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.*
class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
companion object {
private const val sessionContactTable = "session_contact_database"
const val sessionID = "session_id"
const val name = "name"
const val nickname = "nickname"
const val profilePictureURL = "profile_picture_url"
const val profilePictureFileName = "profile_picture_file_name"
const val profilePictureEncryptionKey = "profile_picture_encryption_key"
const val threadID = "thread_id"
const val isTrusted = "is_trusted"
@JvmStatic val createSessionContactTableCommand =
"CREATE TABLE $sessionContactTable " +
"($sessionID STRING PRIMARY KEY, " +
"$name TEXT DEFAULT NULL, " +
"$nickname TEXT DEFAULT NULL, " +
"$profilePictureURL TEXT DEFAULT NULL, " +
"$profilePictureFileName TEXT DEFAULT NULL, " +
"$profilePictureEncryptionKey BLOB DEFAULT NULL, " +
"$threadID INTEGER DEFAULT -1, " +
"$isTrusted INTEGER DEFAULT 0);"
}
fun getContactWithSessionID(sessionID: String): Contact? {
val database = databaseHelper.readableDatabase
return database.get(sessionContactTable, "${SessionContactDatabase.sessionID} = ?", arrayOf( sessionID )) { cursor ->
contactFromCursor(cursor)
}
}
fun getAllContacts(): Set<Contact> {
val database = databaseHelper.readableDatabase
return database.getAll(sessionContactTable, null, null) { cursor ->
contactFromCursor(cursor)
}.toSet()
}
fun setContact(contact: Contact) {
val database = databaseHelper.writableDatabase
val contentValues = ContentValues(8)
contentValues.put(sessionID, contact.sessionID)
contentValues.put(name, contact.name)
contentValues.put(nickname, contact.nickname)
contentValues.put(profilePictureURL, contact.profilePictureURL)
contentValues.put(profilePictureFileName, contact.profilePictureFileName)
contact.profilePictureEncryptionKey?.let {
contentValues.put(profilePictureEncryptionKey, Base64.encodeBytes(it))
}
contentValues.put(threadID, threadID)
contentValues.put(isTrusted, if (contact.isTrusted) 1 else 0)
database.insertOrUpdate(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
notifyConversationListListeners()
}
private fun contactFromCursor(cursor: Cursor): Contact {
val sessionID = cursor.getString(sessionID)
val contact = Contact(sessionID)
contact.name = cursor.getStringOrNull(name)
contact.nickname = cursor.getStringOrNull(nickname)
contact.profilePictureURL = cursor.getStringOrNull(profilePictureURL)
contact.profilePictureFileName = cursor.getStringOrNull(profilePictureFileName)
cursor.getStringOrNull(profilePictureEncryptionKey)?.let {
contact.profilePictureEncryptionKey = Base64.decode(it)
}
contact.threadID = cursor.getLong(threadID)
contact.isTrusted = cursor.getInt(isTrusted) != 0
return contact
}
}

View File

@ -1,54 +0,0 @@
package org.thoughtcrime.securesms.loki.dialogs
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.fragment_key_pair_migration_bottom_sheet.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext
class KeyPairMigrationBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_key_pair_migration_bottom_sheet, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
upgradeNowButton.setOnClickListener { upgradeNow() }
upgradeLaterButton.setOnClickListener { upgradeLater() }
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
val d = dialog as BottomSheetDialog
val bottomSheet = d.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)!!
BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED
BottomSheetBehavior.from(bottomSheet).isHideable = false
}
isCancelable = false
return dialog
}
private fun upgradeNow() {
val applicationContext = requireContext().applicationContext as ApplicationContext
applicationContext.clearAllData(true)
}
private fun upgradeLater() {
val dialog = AlertDialog.Builder(requireContext())
dialog.setMessage("You won't be able to send or receive messages until you upgrade.")
dialog.setPositiveButton(R.string.ok) { _, _ ->
dismiss()
}
dialog.create().show()
}
}

View File

@ -1,55 +0,0 @@
package org.thoughtcrime.securesms.loki.dialogs
import android.app.AlertDialog
import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.fragment_key_pair_migration_success_bottom_sheet.*
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
class KeyPairMigrationSuccessBottomSheet : BottomSheetDialogFragment() {
private val sessionID by lazy {
TextSecurePreferences.getLocalNumber(requireContext())
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_key_pair_migration_success_bottom_sheet, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sessionIDTextView.text = sessionID
copyButton.setOnClickListener { copySessionID() }
okButton.setOnClickListener { dismiss() }
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
// Expand the bottom sheet by default
dialog.setOnShowListener {
val d = dialog as BottomSheetDialog
val bottomSheet = d.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
BottomSheetBehavior.from(bottomSheet!!).setState(BottomSheetBehavior.STATE_EXPANDED);
}
return dialog
}
private fun copySessionID() {
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Session ID", sessionID)
clipboard.setPrimaryClip(clip)
Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
}

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.loki.dialogs package org.thoughtcrime.securesms.loki.dialogs
import android.annotation.SuppressLint
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
@ -8,14 +9,19 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.* import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.*
import kotlinx.android.synthetic.main.view_conversation.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.SSKEnvironment
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
public class UserDetailsBottomSheet : BottomSheetDialogFragment() { class UserDetailsBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_user_details_bottom_sheet, container, false) return inflater.inflate(R.layout.fragment_user_details_bottom_sheet, container, false)
@ -24,11 +30,38 @@ public class UserDetailsBottomSheet : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val publicKey = arguments?.getString("publicKey") ?: return dismiss() val publicKey = arguments?.getString("publicKey") ?: return dismiss()
val recipient = Recipient.from(requireContext(), Address.fromSerialized(publicKey), false)
profilePictureView.publicKey = publicKey profilePictureView.publicKey = publicKey
profilePictureView.glide = GlideApp.with(this) profilePictureView.glide = GlideApp.with(this)
profilePictureView.isLarge = true profilePictureView.isLarge = true
profilePictureView.update() profilePictureView.update()
nameTextView.text = DatabaseFactory.getLokiUserDatabase(requireContext()).getDisplayName(publicKey) ?: "Anonymous" nameTextViewContainer.visibility = View.VISIBLE
nameTextViewContainer.setOnClickListener {
nameTextViewContainer.visibility = View.INVISIBLE
nameEditTextContainer.visibility = View.VISIBLE
nicknameEditText.text = null
nicknameEditText.requestFocus()
showSoftKeyboard()
}
cancelNicknameEditingButton.setOnClickListener {
nicknameEditText.clearFocus()
hideSoftKeyboard()
nameTextViewContainer.visibility = View.VISIBLE
nameEditTextContainer.visibility = View.INVISIBLE
}
saveNicknameButton.setOnClickListener {
saveNickName(recipient)
}
nicknameEditText.setOnEditorActionListener { _, actionId, _ ->
when (actionId) {
EditorInfo.IME_ACTION_DONE -> {
saveNickName(recipient)
return@setOnEditorActionListener true
}
else -> return@setOnEditorActionListener false
}
}
nameTextView.text = recipient.name ?: publicKey // Uses the Contact API internally
publicKeyTextView.text = publicKey publicKeyTextView.text = publicKey
copyButton.setOnClickListener { copyButton.setOnClickListener {
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@ -37,4 +70,32 @@ public class UserDetailsBottomSheet : BottomSheetDialogFragment() {
Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
} }
} }
fun saveNickName(recipient: Recipient) {
nicknameEditText.clearFocus()
hideSoftKeyboard()
nameTextViewContainer.visibility = View.VISIBLE
nameEditTextContainer.visibility = View.INVISIBLE
var newNickName: String? = null
if (nicknameEditText.text.isNotEmpty()) {
newNickName = nicknameEditText.text.toString()
}
val publicKey = recipient.address.serialize()
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
val contact = contactDB.getContactWithSessionID(publicKey) ?: Contact(publicKey)
contact.nickname = newNickName
contactDB.setContact(contact)
nameTextView.text = recipient.name ?: publicKey // Uses the Contact API internally
}
@SuppressLint("ServiceCast")
fun showSoftKeyboard() {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.showSoftInput(nicknameEditText, 0)
}
fun hideSoftKeyboard() {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.hideSoftInputFromWindow(nicknameEditText.windowToken, 0)
}
} }

View File

@ -1,15 +1,8 @@
package org.thoughtcrime.securesms.loki.protocol package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import org.thoughtcrime.securesms.ApplicationContext
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.messages.SignalServiceContent
import org.session.libsignal.messages.SignalServiceDataMessage import org.session.libsignal.messages.SignalServiceDataMessage
import java.security.MessageDigest
object SessionMetaProtocol { object SessionMetaProtocol {
@ -39,19 +32,6 @@ object SessionMetaProtocol {
return shouldIgnoreMessage return shouldIgnoreMessage
} }
@JvmStatic
fun handleProfileUpdateIfNeeded(context: Context, content: SignalServiceContent) {
val displayName = content.senderDisplayName.orNull() ?: return
if (displayName.isBlank()) { return }
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val sender = content.sender.toLowerCase()
if (userPublicKey == sender) {
// Update the user's local name if the message came from their master device
TextSecurePreferences.setProfileName(context, displayName)
}
DatabaseFactory.getLokiUserDatabase(context).setDisplayName(sender, displayName)
}
@JvmStatic @JvmStatic
fun canUserReplyToNotification(recipient: Recipient): Boolean { fun canUserReplyToNotification(recipient: Recipient): Boolean {
// TODO return !recipient.address.isRSSFeed // TODO return !recipient.address.isRSSFeed

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.loki.utilities package org.thoughtcrime.securesms.loki.utilities
import android.content.ContentValues import android.content.ContentValues
import androidx.core.database.getStringOrNull
import net.sqlcipher.Cursor import net.sqlcipher.Cursor
import net.sqlcipher.database.SQLiteDatabase import net.sqlcipher.database.SQLiteDatabase
import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Base64
@ -57,3 +58,7 @@ fun Cursor.getLong(columnName: String): Long {
fun Cursor.getBase64EncodedData(columnName: String): ByteArray { fun Cursor.getBase64EncodedData(columnName: String): ByteArray {
return Base64.decode(getString(columnName)) return Base64.decode(getString(columnName))
} }
fun Cursor.getStringOrNull(columnName: String): String? {
return getStringOrNull(getColumnIndexOrThrow(columnName))
}

View File

@ -15,7 +15,7 @@ object MentionManagerUtilities {
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() } val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() }
result.addAll(members) result.addAll(members)
} else { } else {
if (MentionsManager.shared.userPublicKeyCache[threadID] != null) { return } if (MentionsManager.userPublicKeyCache[threadID] != null) { return }
val messageDatabase = DatabaseFactory.getMmsSmsDatabase(context) val messageDatabase = DatabaseFactory.getMmsSmsDatabase(context)
val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID)) val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID))
var record: MessageRecord? = reader.next var record: MessageRecord? = reader.next
@ -30,6 +30,6 @@ object MentionManagerUtilities {
reader.close() reader.close()
result.add(TextSecurePreferences.getLocalNumber(context)!!) result.add(TextSecurePreferences.getLocalNumber(context)!!)
} }
MentionsManager.shared.userPublicKeyCache[threadID] = result MentionsManager.userPublicKeyCache[threadID] = result
} }
} }

View File

@ -9,6 +9,7 @@ import android.text.style.StyleSpan
import android.util.Range import android.util.Range
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.combine.Tuple2 import nl.komponents.kovenant.combine.Tuple2
import org.session.libsession.messaging.contacts.Contact
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import java.util.regex.Pattern import java.util.regex.Pattern
@ -23,6 +24,8 @@ object MentionUtilities {
@JvmStatic @JvmStatic
fun highlightMentions(text: CharSequence, isOutgoingMessage: Boolean, threadID: Long, context: Context): SpannableString { fun highlightMentions(text: CharSequence, isOutgoingMessage: Boolean, threadID: Long, context: Context): SpannableString {
var text = text var text = text
val threadDB = DatabaseFactory.getThreadDatabase(context)
val isOpenGroup = threadDB.getRecipientForThreadId(threadID)?.isOpenGroupRecipient ?: false
val pattern = Pattern.compile("@[0-9a-fA-F]*") val pattern = Pattern.compile("@[0-9a-fA-F]*")
var matcher = pattern.matcher(text) var matcher = pattern.matcher(text)
val mentions = mutableListOf<Tuple2<Range<Int>, String>>() val mentions = mutableListOf<Tuple2<Range<Int>, String>>()
@ -31,10 +34,12 @@ object MentionUtilities {
if (matcher.find(startIndex)) { if (matcher.find(startIndex)) {
while (true) { while (true) {
val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @ val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
val userDisplayName: String? = if (publicKey.toLowerCase() == userPublicKey.toLowerCase()) { val userDisplayName: String? = if (publicKey.equals(userPublicKey, ignoreCase = true)) {
TextSecurePreferences.getProfileName(context) TextSecurePreferences.getProfileName(context)
} else { } else {
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey)
val context = if (isOpenGroup) Contact.ContactContext.OPEN_GROUP else Contact.ContactContext.REGULAR
contact?.displayName(context)
} }
if (userDisplayName != null) { if (userDisplayName != null) {
text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length) text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length)

View File

@ -1,13 +0,0 @@
@file:JvmName("NotificationUtilities")
package org.thoughtcrime.securesms.loki.utilities
import android.content.Context
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.session.libsession.utilities.recipients.Recipient
fun getOpenGroupDisplayName(recipient: Recipient, threadRecipient: Recipient, context: Context): String {
val publicKey = recipient.address.toString()
val displayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
// FIXME: Add short ID here?
return displayName ?: publicKey
}

View File

@ -2,13 +2,15 @@ package org.thoughtcrime.securesms.loki.views
import android.content.Context import android.content.Context
import android.graphics.Typeface import android.graphics.Typeface
import android.text.TextUtils
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_conversation.view.* import kotlinx.android.synthetic.main.view_conversation.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.SSKEnvironment
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded
@ -56,7 +58,7 @@ class ConversationView : LinearLayout {
} }
profilePictureView.glide = glide profilePictureView.glide = glide
profilePictureView.update(thread.recipient, thread.threadId) profilePictureView.update(thread.recipient, thread.threadId)
val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else if (!thread.recipient.name.isNullOrEmpty()) thread.recipient.name else thread.recipient.address.toString() val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString()
btnGroupNameDisplay.text = senderDisplayName btnGroupNameDisplay.text = senderDisplayName
timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date) timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date)
muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE
@ -85,9 +87,12 @@ class ConversationView : LinearLayout {
profilePictureView.recycle() profilePictureView.recycle()
} }
private fun getUserDisplayName(publicKey: String?): String? { private fun getUserDisplayName(recipient: Recipient): String? {
if (TextUtils.isEmpty(publicKey)) return null if (recipient.isLocalNumber) {
return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!) return context.getString(R.string.note_to_self)
} else {
return recipient.name // Internally uses the Contact API
}
} }
// endregion // endregion
} }

View File

@ -11,6 +11,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import kotlinx.android.synthetic.main.view_profile_picture.view.* import kotlinx.android.synthetic.main.view_profile_picture.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.avatars.ProfileContactPhoto import org.session.libsession.avatars.ProfileContactPhoto
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.mentions.MentionsManager import org.session.libsession.messaging.mentions.MentionsManager
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
@ -57,19 +58,15 @@ class ProfilePictureView : RelativeLayout {
// region Updating // region Updating
fun update(recipient: Recipient, threadID: Long) { fun update(recipient: Recipient, threadID: Long) {
fun getUserDisplayName(publicKey: String?): String? { fun getUserDisplayName(publicKey: String): String {
if (publicKey == null || publicKey.isBlank()) { val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey)
return null return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
} else {
val result = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
return result ?: publicKey
}
} }
fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean { fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean {
return recipient.isOpenGroupRecipient && recipient.groupAvatarId != null return recipient.isOpenGroupRecipient && recipient.groupAvatarId != null
} }
if (recipient.isGroupRecipient && !isOpenGroupWithProfilePicture(recipient)) { if (recipient.isGroupRecipient && !isOpenGroupWithProfilePicture(recipient)) {
val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf() val users = MentionsManager.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf()
users.remove(TextSecurePreferences.getLocalNumber(context)) users.remove(TextSecurePreferences.getLocalNumber(context))
val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability
if (users.count() == 1) { if (users.count() == 1) {
@ -86,7 +83,8 @@ class ProfilePictureView : RelativeLayout {
recipient.name == "Session Updates" || recipient.name == "Session Updates" ||
recipient.name == "Session Public Chat" recipient.name == "Session Public Chat"
} else { } else {
publicKey = recipient.address.toString() val publicKey = recipient.address.toString()
this.publicKey = publicKey
displayName = getUserDisplayName(publicKey) displayName = getUserDisplayName(publicKey)
additionalPublicKey = null additionalPublicKey = null
isRSSFeed = false isRSSFeed = false

View File

@ -8,6 +8,7 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView
import kotlinx.android.synthetic.main.view_user.view.* import kotlinx.android.synthetic.main.view_user.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
@ -48,13 +49,9 @@ class UserView : LinearLayout {
// region Updating // region Updating
fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) { fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) {
fun getUserDisplayName(publicKey: String?): String? { fun getUserDisplayName(publicKey: String): String {
if (publicKey == null || publicKey.isBlank()) { val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey)
return null return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
} else {
val result = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
return result ?: publicKey
}
} }
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(user) val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(user)
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadID, context) // FIXME: This is a bad place to do this MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadID, context) // FIXME: This is a bad place to do this

View File

@ -8,14 +8,14 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import org.session.libsession.messaging.contacts.Contact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.utilities.NotificationUtilities; import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
import org.session.libsession.utilities.NotificationPrivacyPreference; import org.session.libsession.utilities.NotificationPrivacyPreference;
import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -49,8 +49,15 @@ public class MultipleRecipientNotificationBuilder extends AbstractNotificationBu
public void setMostRecentSender(Recipient recipient, Recipient threadRecipient) { public void setMostRecentSender(Recipient recipient, Recipient threadRecipient) {
String displayName = recipient.toShortString(); String displayName = recipient.toShortString();
if (threadRecipient.isGroupRecipient()) { if (threadRecipient.isOpenGroupRecipient()) {
displayName = NotificationUtilities.getOpenGroupDisplayName(recipient, threadRecipient, context); SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(context);
String sessionID = recipient.getAddress().serialize();
Contact contact = contactDB.getContactWithSessionID(sessionID);
if (contact != null) {
displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP);
} else {
displayName = sessionID;
}
} }
if (privacy.isDisplayContact()) { if (privacy.isDisplayContact()) {
setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s, displayName)); setContentText(context.getString(R.string.MessageNotifier_most_recent_from_s, displayName));
@ -71,8 +78,15 @@ public class MultipleRecipientNotificationBuilder extends AbstractNotificationBu
public void addMessageBody(@NonNull Recipient sender, Recipient threadRecipient, @Nullable CharSequence body) { public void addMessageBody(@NonNull Recipient sender, Recipient threadRecipient, @Nullable CharSequence body) {
String displayName = sender.toShortString(); String displayName = sender.toShortString();
if (threadRecipient.isGroupRecipient()) { if (threadRecipient.isOpenGroupRecipient()) {
displayName = NotificationUtilities.getOpenGroupDisplayName(sender, threadRecipient, context); SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(context);
String sessionID = sender.getAddress().serialize();
Contact contact = contactDB.getContactWithSessionID(sessionID);
if (contact != null) {
displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP);
} else {
displayName = sessionID;
}
} }
if (privacy.isDisplayMessage()) { if (privacy.isDisplayMessage()) {
SpannableStringBuilder builder = new SpannableStringBuilder(); SpannableStringBuilder builder = new SpannableStringBuilder();

View File

@ -15,22 +15,21 @@ import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationCompat.Action; import androidx.core.app.NotificationCompat.Action;
import androidx.core.app.RemoteInput; import androidx.core.app.RemoteInput;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.session.libsession.avatars.ContactColors; import org.session.libsession.avatars.ContactColors;
import org.session.libsession.avatars.ContactPhoto; import org.session.libsession.avatars.ContactPhoto;
import org.session.libsession.avatars.GeneratedContactPhoto; import org.session.libsession.avatars.GeneratedContactPhoto;
import org.session.libsession.messaging.contacts.Contact;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator; import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator;
import org.thoughtcrime.securesms.loki.utilities.NotificationUtilities;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
@ -40,11 +39,9 @@ import org.session.libsession.utilities.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import network.loki.messenger.R; import network.loki.messenger.R;
public class SingleRecipientNotificationBuilder extends AbstractNotificationBuilder { public class SingleRecipientNotificationBuilder extends AbstractNotificationBuilder {
@ -121,8 +118,16 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
{ {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && threadRecipients.isGroupRecipient()) { if (privacy.isDisplayContact() && threadRecipients.isOpenGroupRecipient()) {
String displayName = NotificationUtilities.getOpenGroupDisplayName(individualRecipient, threadRecipients, context); String displayName;
SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(context);
String sessionID = individualRecipient.getAddress().serialize();
Contact contact = contactDB.getContactWithSessionID(sessionID);
if (contact != null) {
displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP);
} else {
displayName = sessionID;
}
if (displayName != null) { if (displayName != null) {
stringBuilder.append(Util.getBoldedString(displayName + ": ")); stringBuilder.append(Util.getBoldedString(displayName + ": "));
} }
@ -209,8 +214,16 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
{ {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && threadRecipient.isGroupRecipient()) { if (privacy.isDisplayContact() && threadRecipient.isOpenGroupRecipient()) {
String displayName = NotificationUtilities.getOpenGroupDisplayName(individualRecipient, threadRecipient, context); String displayName;
SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(context);
String sessionID = individualRecipient.getAddress().serialize();
Contact contact = contactDB.getContactWithSessionID(sessionID);
if (contact != null) {
displayName = contact.displayName(Contact.ContactContext.OPEN_GROUP);
} else {
displayName = sessionID;
}
if (displayName != null) { if (displayName != null) {
stringBuilder.append(Util.getBoldedString(displayName + ": ")); stringBuilder.append(Util.getBoldedString(displayName + ": "));
} }

View File

@ -1,22 +1,70 @@
package org.thoughtcrime.securesms.sskenvironment package org.thoughtcrime.securesms.sskenvironment
import android.content.Context import android.content.Context
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.SSKEnvironment
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
class ProfileManager: SSKEnvironment.ProfileManagerProtocol { class ProfileManager : SSKEnvironment.ProfileManagerProtocol {
override fun setDisplayName(context: Context, recipient: Recipient, displayName: String) {
DatabaseFactory.getLokiUserDatabase(context).setDisplayName(recipient.address.serialize(), displayName) override fun setNickname(context: Context, recipient: Recipient, nickname: String?) {
val sessionID = recipient.address.serialize()
val contactDatabase = DatabaseFactory.getSessionContactDatabase(context)
var contact = contactDatabase.getContactWithSessionID(sessionID)
if (contact == null) contact = Contact(sessionID)
contact.threadID = DatabaseFactory.getStorage(context).getThreadId(recipient.address)
if (contact.nickname != nickname) {
contact.nickname = nickname
contactDatabase.setContact(contact)
}
}
override fun setName(context: Context, recipient: Recipient, name: String) {
// New API
val sessionID = recipient.address.serialize()
val contactDatabase = DatabaseFactory.getSessionContactDatabase(context)
var contact = contactDatabase.getContactWithSessionID(sessionID)
if (contact == null) contact = Contact(sessionID)
contact.threadID = DatabaseFactory.getStorage(context).getThreadId(recipient.address)
if (contact.name != name) {
contact.name = name
contactDatabase.setContact(contact)
}
// Old API
val database = DatabaseFactory.getRecipientDatabase(context)
database.setProfileName(recipient, name)
recipient.notifyListeners()
} }
override fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String) { override fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String) {
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(recipient, profilePictureURL)) val job = RetrieveProfileAvatarJob(recipient, profilePictureURL)
ApplicationContext.getInstance(context).jobManager.add(job)
val sessionID = recipient.address.serialize()
val contactDatabase = DatabaseFactory.getSessionContactDatabase(context)
var contact = contactDatabase.getContactWithSessionID(sessionID)
if (contact == null) contact = Contact(sessionID)
contact.threadID = DatabaseFactory.getStorage(context).getThreadId(recipient.address)
if (contact.profilePictureURL != profilePictureURL) {
contact.profilePictureURL = profilePictureURL
contactDatabase.setContact(contact)
}
} }
override fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray) { override fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray) {
// New API
val sessionID = recipient.address.serialize()
val contactDatabase = DatabaseFactory.getSessionContactDatabase(context)
var contact = contactDatabase.getContactWithSessionID(sessionID)
if (contact == null) contact = Contact(sessionID)
contact.threadID = DatabaseFactory.getStorage(context).getThreadId(recipient.address)
if (!contact.profilePictureEncryptionKey.contentEquals(profileKey)) {
contact.profilePictureEncryptionKey = profileKey
contactDatabase.setContact(contact)
}
// Old API
val database = DatabaseFactory.getRecipientDatabase(context) val database = DatabaseFactory.getRecipientDatabase(context)
database.setProfileKey(recipient, profileKey) database.setProfileKey(recipient, profileKey)
} }

View File

@ -59,6 +59,6 @@
android:background="@color/transparent" android:background="@color/transparent"
android:textAllCaps="false" android:textAllCaps="false"
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:text="Link a Device" /> android:text="@string/activity_link_device_link_device" />
</LinearLayout> </LinearLayout>

View File

@ -19,7 +19,7 @@
android:textSize="@dimen/very_large_font_size" android:textSize="@dimen/very_large_font_size"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Message Notifications" /> android:text="@string/activity_pn_mode_message_notifications" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +29,7 @@
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="There are two ways Session can notify you of new messages." /> android:text="@string/activity_pn_mode_explanation" />
<org.thoughtcrime.securesms.loki.views.PNModeView <org.thoughtcrime.securesms.loki.views.PNModeView
android:id="@+id/fcmOptionView" android:id="@+id/fcmOptionView"
@ -48,7 +48,7 @@
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
android:text="Fast Mode" /> android:text="@string/activity_pn_mode_fast_mode" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -56,7 +56,7 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Youll be notified of new messages reliably and immediately using Googles notification servers." /> android:text="@string/activity_pn_mode_fast_mode_explanation" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -86,7 +86,7 @@
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
android:text="Slow Mode" /> android:text="@string/activity_pn_mode_slow_mode" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -94,7 +94,7 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Session will occasionally check for new messages in the background." /> android:text="@string/activity_pn_mode_slow_mode_explanation" />
</org.thoughtcrime.securesms.loki.views.PNModeView> </org.thoughtcrime.securesms.loki.views.PNModeView>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -69,6 +70,7 @@
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:textColorLink="?colorAccent" android:textColorLink="?colorAccent"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:text="By using this service, you agree to our Terms of Service and Privacy Policy" /> <!-- Intentionally not yet translated --> android:text="By using this service, you agree to our Terms of Service and Privacy Policy"
tools:ignore="HardcodedText" /> <!-- Intentionally not yet translated -->
</LinearLayout> </LinearLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -38,7 +39,7 @@
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="@dimen/medium_spacing" android:layout_marginTop="@dimen/medium_spacing"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
@ -74,6 +75,7 @@
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:textColorLink="?colorAccent" android:textColorLink="?colorAccent"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:text="By using this service, you agree to our Terms of Service and Privacy Policy" /> <!-- Intentionally not yet translated --> android:text="By using this service, you agree to our Terms of Service and Privacy Policy"
tools:ignore="HardcodedText" /> <!-- Intentionally not yet translated -->
</LinearLayout> </LinearLayout>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout 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:tools="http://schemas.android.com/tools"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
@ -47,7 +47,7 @@
android:gravity="center" android:gravity="center"
android:textSize="16sp" android:textSize="16sp"
android:textAlignment="center" android:textAlignment="center"
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" /> tools:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
<TextView <TextView
android:id="@+id/revealButton" android:id="@+id/revealButton"

View File

@ -18,7 +18,7 @@
android:textSize="@dimen/very_large_font_size" android:textSize="@dimen/very_large_font_size"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Recovery Phrase" /> android:text="@string/fragment_recovery_phrase_title" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -28,7 +28,7 @@
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="To link your device, enter the recovery phrase that was given to you when you signed up." /> android:text="@string/activity_restore_explanation" />
<EditText <EditText
style="@style/SessionEditText" style="@style/SessionEditText"

View File

@ -36,7 +36,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textStyle="bold" android:textStyle="bold"
android:text="You're almost finished! 80%" /> android:text="@string/view_seed_reminder_title" />
<TextView <TextView
android:id="@+id/subtitleTextView" android:id="@+id/subtitleTextView"
@ -47,7 +47,7 @@
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:lines="2" android:lines="2"
android:alpha="0.6" android:alpha="0.6"
android:text="Secure your account by saving your recovery phrase" /> android:text="@string/view_seed_reminder_subtitle_1" />
</LinearLayout> </LinearLayout>
@ -64,7 +64,7 @@
android:layout_height="28dp" android:layout_height="28dp"
android:layout_marginLeft="4dp" android:layout_marginLeft="4dp"
android:textStyle="normal" android:textStyle="normal"
android:text="Continue" /> android:text="@string/continue_2" />
</LinearLayout> </LinearLayout>

View File

@ -99,7 +99,8 @@
android:text="By using this service, you agree to our Terms of Service and Privacy Policy" android:text="By using this service, you agree to our Terms of Service and Privacy Policy"
android:textColor="@color/text" android:textColor="@color/text"
android:textColorLink="@color/text" android:textColorLink="@color/text"
android:textSize="@dimen/very_small_font_size" /> <!-- Intentionally not yet translated --> android:textSize="@dimen/very_small_font_size"
tools:ignore="HardcodedText" /> <!-- Intentionally not yet translated -->
</LinearLayout> </LinearLayout>

View File

@ -42,7 +42,7 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginLeft="64dp" android:layout_marginLeft="64dp"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:text="Session" android:text="@string/app_name"
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/very_large_font_size" /> android:textSize="@dimen/very_large_font_size" />

View File

@ -59,6 +59,6 @@
android:background="@color/transparent" android:background="@color/transparent"
android:textAllCaps="false" android:textAllCaps="false"
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:text="Link a Device" /> android:text="@string/activity_link_device_link_device" />
</LinearLayout> </LinearLayout>

View File

@ -18,7 +18,7 @@
android:textSize="@dimen/large_font_size" android:textSize="@dimen/large_font_size"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Community Guidelines" /> android:text="@string/ConversationActivity_open_group_guidelines" />
<TextView <TextView
android:id="@+id/communityGuidelinesTextView" android:id="@+id/communityGuidelinesTextView"

View File

@ -19,7 +19,7 @@
android:textSize="@dimen/large_font_size" android:textSize="@dimen/large_font_size"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Message Notifications" /> android:text="@string/activity_pn_mode_message_notifications" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -29,7 +29,7 @@
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="There are two ways Session can notify you of new messages." /> android:text="@string/activity_pn_mode_explanation" />
<org.thoughtcrime.securesms.loki.views.PNModeView <org.thoughtcrime.securesms.loki.views.PNModeView
android:id="@+id/fcmOptionView" android:id="@+id/fcmOptionView"
@ -48,7 +48,7 @@
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
android:text="Fast Mode" /> android:text="@string/activity_pn_mode_fast_mode" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -56,7 +56,7 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Youll be notified of new messages reliably and immediately using Googles notification servers." /> android:text="@string/activity_pn_mode_fast_mode_explanation" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -86,7 +86,7 @@
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
android:text="Slow Mode" /> android:text="@string/activity_pn_mode_slow_mode" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -94,7 +94,7 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Session will occasionally check for new messages in the background." /> android:text="@string/activity_pn_mode_slow_mode_explanation" />
</org.thoughtcrime.securesms.loki.views.PNModeView> </org.thoughtcrime.securesms.loki.views.PNModeView>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -69,6 +70,7 @@
android:textColorLink="@color/text" android:textColorLink="@color/text"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:text="By using this service, you agree to our Terms of Service and Privacy Policy" /> <!-- Intentionally not yet translated --> android:text="By using this service, you agree to our Terms of Service and Privacy Policy"
tools:ignore="HardcodedText" /> <!-- Intentionally not yet translated -->
</LinearLayout> </LinearLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -35,11 +36,11 @@
android:id="@+id/publicKeyTextView" android:id="@+id/publicKeyTextView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18dp" android:textSize="18sp"
android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing" android:layout_marginRight="@dimen/very_large_spacing"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
@ -75,6 +76,7 @@
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:textColorLink="?colorAccent" android:textColorLink="?colorAccent"
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:text="By using this service, you agree to our Terms of Service and Privacy Policy" /> <!-- Intentionally not yet translated --> android:text="By using this service, you agree to our Terms of Service and Privacy Policy"
tools:ignore="HardcodedText" /> <!-- Intentionally not yet translated -->
</LinearLayout> </LinearLayout>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout 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:tools="http://schemas.android.com/tools"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
@ -47,7 +47,7 @@
android:gravity="center" android:gravity="center"
android:textSize="14sp" android:textSize="14sp"
android:textAlignment="center" android:textAlignment="center"
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" /> tools:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
<TextView <TextView
android:id="@+id/revealButton" android:id="@+id/revealButton"

View File

@ -76,7 +76,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:fontFamily="@font/space_mono_regular" android:fontFamily="@font/space_mono_regular"
android:textAlignment="center" android:textAlignment="center"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -211,7 +211,7 @@
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textStyle="bold" android:textStyle="bold"
android:gravity="center" android:gravity="center"
android:text="Help us Translate Session" /> android:text="@string/activity_settings_help_translate_session" />
<TextView <TextView
android:id="@+id/versionTextView" android:id="@+id/versionTextView"

View File

@ -13,7 +13,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="You don't have any contacts yet" android:text="@string/fragment_contact_selection_empty_contacts"
android:textSize="@dimen/medium_font_size" /> android:textSize="@dimen/medium_font_size" />
</LinearLayout> </LinearLayout>

View File

@ -55,9 +55,10 @@
android:id="@+id/titleTextView" android:id="@+id/titleTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@null"
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:text="Conversation" tools:text="Conversation"
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/large_font_size" android:textSize="@dimen/large_font_size"
android:fontFamily="sans-serif-medium" /> android:fontFamily="sans-serif-medium" />
@ -83,7 +84,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:text="26 members" tools:text="26 members"
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/small_font_size" /> android:textSize="@dimen/small_font_size" />
@ -217,7 +218,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:paddingStart="5dip" android:paddingStart="5dip"
android:text="160/160 (1)" tools:text="160/160 (1)"
android:visibility="gone" /> android:visibility="gone" />
<ViewStub <ViewStub

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout 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:tools="http://schemas.android.com/tools"
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"
@ -27,7 +27,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing" android:layout_marginTop="@dimen/large_spacing"
android:padding="@dimen/small_spacing" android:padding="@dimen/small_spacing"
android:text="habitat kiwi amply iceberg dog nerves spiderman soft match partial awakened maximum degrees" tools:text="habitat kiwi amply iceberg dog nerves spiderman soft match partial awakened maximum degrees"
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textAlignment="center" /> android:textAlignment="center" />

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contentView" android:id="@+id/contentView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -60,7 +60,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:fontFamily="@font/space_mono_regular" android:fontFamily="@font/space_mono_regular"
android:textAlignment="center" android:textAlignment="center"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:padding="@dimen/large_spacing"
app:behavior_hideable="false"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_shield"
android:tint="?android:textColorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="Session IDs Just Got Better"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="Weve upgraded Session IDs to make them even more private and secure. To ensure your continued privacy you're now required to upgrade."
android:textSize="@dimen/medium_font_size"
android:textAlignment="center" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_spacing"
android:text="Your existing contacts and conversations will be lost, but youll be able to use Session knowing you have the best privacy and security possible."
android:textSize="@dimen/medium_font_size"
android:textAlignment="center" />
<Button
style="@style/Widget.Session.Button.Common.UnimportantOutline"
android:id="@+id/upgradeNowButton"
android:layout_width="240dp"
android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/very_large_spacing"
android:text="Upgrade Now" />
<Button
style="@style/Widget.Session.Button.Common.UnimportantOutline"
android:id="@+id/upgradeLaterButton"
android:layout_width="240dp"
android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/medium_spacing"
android:text="Upgrade Later" />
</LinearLayout>

View File

@ -1,61 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:padding="@dimen/large_spacing"
app:behavior_hideable="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_shield"
android:tint="?android:textColorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="Upgrade Successful!"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="Your new and improved Session ID is:"
android:textSize="@dimen/medium_font_size"
android:textAlignment="center" />
<TextView
style="@style/SessionIDTextView"
android:id="@+id/sessionIDTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18dp"
android:layout_marginTop="@dimen/medium_spacing"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<Button
style="@style/Widget.Session.Button.Common.UnimportantOutline"
android:id="@+id/copyButton"
android:layout_width="240dp"
android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/very_large_spacing"
android:text="Copy" />
<Button
style="@style/Widget.Session.Button.Common.UnimportantOutline"
android:id="@+id/okButton"
android:layout_width="240dp"
android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/medium_spacing"
android:text="@string/ok" />
</LinearLayout>

View File

@ -18,7 +18,7 @@
android:textSize="@dimen/large_font_size" android:textSize="@dimen/large_font_size"
android:textStyle="bold" android:textStyle="bold"
android:textColor="@color/text" android:textColor="@color/text"
android:text="Recovery Phrase" /> android:text="@string/fragment_recovery_phrase_title" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -30,7 +30,7 @@
android:layout_weight="1" android:layout_weight="1"
android:padding="@dimen/very_large_spacing" android:padding="@dimen/very_large_spacing"
android:gravity="center" android:gravity="center"
android:text="Scan a users QR code to start a session. QR codes can be found by tapping the QR code icon in account settings." android:text="@string/activity_create_private_chat_scan_qr_code_explanation"
android:background="?android:windowBackground" android:background="?android:windowBackground"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textColor="?android:textColorTertiary" /> android:textColor="?android:textColorTertiary" />

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingLeft="@dimen/large_spacing" android:paddingLeft="@dimen/large_spacing"
@ -16,27 +16,97 @@
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"
android:layout_marginTop="@dimen/large_spacing" /> android:layout_marginTop="@dimen/large_spacing"/>
<TextView <RelativeLayout
android:id="@+id/nameTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_spacing" android:layout_marginTop="@dimen/medium_spacing"
android:textSize="@dimen/massive_font_size" android:gravity="center">
android:textStyle="bold"
android:textColor="@color/text" <LinearLayout
android:text="Spiderman" /> android:id="@+id/nameTextViewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_centerInParent="true"
android:visibility="visible">
<View
android:layout_width="24dp"
android:layout_height="24dp" />
<TextView
android:id="@+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="@dimen/small_spacing"
android:layout_marginEnd="@dimen/small_spacing"
android:textSize="@dimen/large_font_size"
android:textStyle="bold"
android:textColor="@color/text"
android:textAlignment="center"
android:text="Spiderman" />
<ImageView
android:layout_width="20dp"
android:layout_height="22dp"
android:paddingTop="2dp"
android:src="@drawable/ic_baseline_edit_24" />
</LinearLayout>
<LinearLayout
android:id="@+id/nameEditTextContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:visibility="invisible">
<ImageView
android:id="@+id/cancelNicknameEditingButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_baseline_clear_24" />
<EditText
android:id="@+id/nicknameEditText"
style="@style/SmallSessionEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="@dimen/small_spacing"
android:layout_marginEnd="@dimen/small_spacing"
android:textAlignment="center"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:inputType="text"
android:singleLine="true"
android:imeOptions="actionDone"
android:hint="@string/fragment_user_details_bottom_sheet_edit_text_hint" />
<ImageView
android:id="@+id/saveNicknameButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_baseline_done_24" />
</LinearLayout>
</RelativeLayout>
<TextView <TextView
style="@style/SessionIDTextView" style="@style/SessionIDTextView"
android:id="@+id/publicKeyTextView" android:id="@+id/publicKeyTextView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="@dimen/large_font_size" android:layout_marginTop="@dimen/medium_spacing"
android:layout_marginTop="@dimen/large_spacing" android:textSize="@dimen/medium_font_size"
android:textIsSelectable="true" android:textIsSelectable="true"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<Button <Button
style="@style/Widget.Session.Button.Common.ProminentOutline" style="@style/Widget.Session.Button.Common.ProminentOutline"

View File

@ -32,7 +32,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
android:textAlignment="center" android:textAlignment="center"
android:text="Scan Me" /> android:text="@string/fragment_view_my_qr_code_title" />
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -59,7 +59,7 @@
android:textSize="@dimen/medium_font_size" android:textSize="@dimen/medium_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:textAlignment="center" android:textAlignment="center"
android:text="This is your QR code. Other users can scan it to start a session with you." /> android:text="@string/fragment_view_my_qr_code_explanation" />
<Button <Button
style="@style/Widget.Session.Button.Common.UnimportantOutline" style="@style/Widget.Session.Button.Common.UnimportantOutline"
@ -68,7 +68,7 @@
android:layout_height="@dimen/medium_button_height" android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="28dp" android:layout_marginTop="28dp"
android:layout_marginBottom="@dimen/medium_spacing" android:layout_marginBottom="@dimen/medium_spacing"
android:text="Share" /> android:text="@string/share" />
</LinearLayout> </LinearLayout>

View File

@ -50,7 +50,7 @@
style="@style/Signal.Text.Body" style="@style/Signal.Text.Body"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Session is Locked" android:text="@string/activity_prompt_passphrase_session_locked"
android:textStyle="bold" android:textStyle="bold"
android:textSize="@dimen/large_font_size" android:textSize="@dimen/large_font_size"
android:textAlignment="center" android:textAlignment="center"
@ -73,7 +73,6 @@
android:id="@+id/lock_screen_auth_container" android:id="@+id/lock_screen_auth_container"
android:layout_width="196dp" android:layout_width="196dp"
android:layout_height="@dimen/medium_button_height" android:layout_height="@dimen/medium_button_height"
android:text="Tap to Unlock"/> android:text="@string/activity_prompt_passphrase_tap_to_unlock"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -23,7 +23,7 @@
<TextView android:layout_width="wrap_content" <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Share" android:text="@string/share"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:textSize="@dimen/very_large_font_size" android:textSize="@dimen/very_large_font_size"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"

View File

@ -63,7 +63,7 @@
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textColor="@color/text" android:textColor="@color/text"
android:alpha="0.4" android:alpha="0.4"
android:text="9:41 AM" /> tools:text="9:41 AM" />
</LinearLayout> </LinearLayout>

View File

@ -33,7 +33,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textStyle="bold" android:textStyle="bold"
android:text="Pinned message" /> android:text="@string/open_group_guidelines_pinned_message" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -42,7 +42,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:maxLines="2" android:maxLines="2"
android:text="Community guidelines" /> android:text="@string/open_group_guidelines_community_guidelines" />
</LinearLayout> </LinearLayout>
@ -60,7 +60,7 @@
android:layout_marginRight="12dp" android:layout_marginRight="12dp"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textStyle="normal" android:textStyle="normal"
android:text="Read" /> android:text="@string/open_group_guidelines_read" />
</LinearLayout> </LinearLayout>

View File

@ -36,7 +36,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:textSize="@dimen/small_font_size" android:textSize="@dimen/small_font_size"
android:textStyle="bold" android:textStyle="bold"
android:text="You're almost finished! 80%" /> android:text="@string/view_seed_reminder_title" />
<TextView <TextView
android:id="@+id/subtitleTextView" android:id="@+id/subtitleTextView"
@ -47,7 +47,7 @@
android:textSize="@dimen/very_small_font_size" android:textSize="@dimen/very_small_font_size"
android:lines="2" android:lines="2"
android:alpha="0.6" android:alpha="0.6"
android:text="Secure your account by saving your recovery phrase" /> android:text="@string/view_seed_reminder_subtitle_1" />
</LinearLayout> </LinearLayout>
@ -64,7 +64,7 @@
android:layout_height="28dp" android:layout_height="28dp"
android:layout_marginLeft="4dp" android:layout_marginLeft="4dp"
android:textStyle="normal" android:textStyle="normal"
android:text="Continue" /> android:text="@string/continue_2" />
</LinearLayout> </LinearLayout>

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<item <item
android:id="@+id/action_filter_search" android:id="@+id/action_filter_search"
android:title=" " app:actionViewClass="androidx.appcompat.widget.SearchView"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="collapseActionView|always"
app:showAsAction="collapseActionView|always" /> android:title="" />
</menu> </menu>

View File

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:title="Learn More" android:title="@string/activity_path_learn_more_button_title"
android:id="@+id/learnMoreButton" android:id="@+id/learnMoreButton"
android:icon="@drawable/ic_info_outline_white_24dp" android:icon="@drawable/ic_info_outline_white_24dp"
app:showAsAction="always" /> app:showAsAction="always" />

View File

@ -73,6 +73,10 @@
<string name="ConversationActivity_select_contact_info">Select contact info</string> <string name="ConversationActivity_select_contact_info">Select contact info</string>
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">Sorry, there was an error setting your attachment.</string> <string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">Sorry, there was an error setting your attachment.</string>
<string name="ConversationActivity_message">Message</string> <string name="ConversationActivity_message">Message</string>
<string name="ConversationActivity_compose">Compose</string>
<string name="ConversationActivity_muted_until_date">Muted until %1$s</string>
<string name="ConversationActivity_member_count">%1$d members</string>
<string name="ConversationActivity_open_group_guidelines">Community Guidelines</string>
<string name="ConversationActivity_invalid_recipient">Invalid recipient!</string> <string name="ConversationActivity_invalid_recipient">Invalid recipient!</string>
<string name="ConversationActivity_added_to_home_screen">Added to home screen</string> <string name="ConversationActivity_added_to_home_screen">Added to home screen</string>
@ -419,6 +423,9 @@
<!-- open_group_invitation_view --> <!-- open_group_invitation_view -->
<string name="open_group_invitation_view__join_accessibility_description">Join</string> <string name="open_group_invitation_view__join_accessibility_description">Join</string>
<string name="open_group_invitation_view__open_group_invitation">Open group invitation</string> <string name="open_group_invitation_view__open_group_invitation">Open group invitation</string>
<string name="open_group_guidelines_pinned_message">Pinned message</string>
<string name="open_group_guidelines_community_guidelines">Community guidelines</string>
<string name="open_group_guidelines_read">Read</string>
<!-- QuoteView --> <!-- QuoteView -->
<string name="QuoteView_audio">Audio</string> <string name="QuoteView_audio">Audio</string>
@ -694,6 +701,7 @@
<string name="activity_seed_explanation">Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone.</string> <string name="activity_seed_explanation">Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone.</string>
<string name="activity_seed_reveal_button_title">Hold to reveal</string> <string name="activity_seed_reveal_button_title">Hold to reveal</string>
<string name="view_seed_reminder_title">You\'re almost finished! 80%</string>
<string name="view_seed_reminder_subtitle_1">Secure your account by saving your recovery phrase</string> <string name="view_seed_reminder_subtitle_1">Secure your account by saving your recovery phrase</string>
<string name="view_seed_reminder_subtitle_2">Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.</string> <string name="view_seed_reminder_subtitle_2">Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.</string>
<string name="view_seed_reminder_subtitle_3">Make sure to store your recovery phrase in a safe place</string> <string name="view_seed_reminder_subtitle_3">Make sure to store your recovery phrase in a safe place</string>
@ -745,6 +753,7 @@
<string name="activity_settings_invite_button_title">Invite</string> <string name="activity_settings_invite_button_title">Invite</string>
<string name="activity_settings_recovery_phrase_button_title">Recovery Phrase</string> <string name="activity_settings_recovery_phrase_button_title">Recovery Phrase</string>
<string name="activity_settings_clear_all_data_button_title">Clear Data</string> <string name="activity_settings_clear_all_data_button_title">Clear Data</string>
<string name="activity_settings_help_translate_session">Help us Translate Session</string>
<string name="activity_notification_settings_title">Notifications</string> <string name="activity_notification_settings_title">Notifications</string>
<string name="activity_notification_settings_style_section_title">Notification Style</string> <string name="activity_notification_settings_style_section_title">Notification Style</string>
@ -769,6 +778,7 @@
<string name="activity_qr_code_view_my_qr_code_tab_title">View My QR Code</string> <string name="activity_qr_code_view_my_qr_code_tab_title">View My QR Code</string>
<string name="activity_qr_code_view_scan_qr_code_tab_title">Scan QR Code</string> <string name="activity_qr_code_view_scan_qr_code_tab_title">Scan QR Code</string>
<string name="activity_qr_code_view_scan_qr_code_explanation">Scan someone\'s QR code to start a conversation with them</string> <string name="activity_qr_code_view_scan_qr_code_explanation">Scan someone\'s QR code to start a conversation with them</string>
<string name="fragment_view_my_qr_code_title">Scan Me</string>
<string name="fragment_view_my_qr_code_explanation">This is your QR code. Other users can scan it to start a session with you.</string> <string name="fragment_view_my_qr_code_explanation">This is your QR code. Other users can scan it to start a session with you.</string>
<string name="fragment_view_my_qr_code_share_title">Share QR Code</string> <string name="fragment_view_my_qr_code_share_title">Share QR Code</string>
@ -776,6 +786,7 @@
<string name="fragment_contact_selection_contacts_title">Contacts</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_closed_groups_title">Closed Groups</string>
<string name="fragment_contact_selection_open_groups_title">Open Groups</string> <string name="fragment_contact_selection_open_groups_title">Open Groups</string>
<string name="fragment_contact_selection_empty_contacts">You don\'t have any contacts yet</string>
<!-- Next round of translation --> <!-- Next round of translation -->
@ -813,8 +824,23 @@
<string name="activity_backup_restore_select_file">Select a file</string> <string name="activity_backup_restore_select_file">Select a file</string>
<string name="activity_backup_restore_explanation_1">Select a backup file and enter the passphrase it was created with.</string> <string name="activity_backup_restore_explanation_1">Select a backup file and enter the passphrase it was created with.</string>
<string name="activity_backup_restore_passphrase">30-digit passphrase</string> <string name="activity_backup_restore_passphrase">30-digit passphrase</string>
<!-- LinkDeviceActivity -->
<string name="activity_link_device_skip_prompt">This is taking a while, would you like to skip?</string> <string name="activity_link_device_skip_prompt">This is taking a while, would you like to skip?</string>
<string name="activity_link_device_link_device">Link a Device</string>
<string name="activity_join_public_chat_join_rooms">Or join one of these…</string> <string name="activity_join_public_chat_join_rooms">Or join one of these…</string>
<string name="activity_pn_mode_message_notifications">Message Notifications</string>
<string name="activity_pn_mode_explanation">There are two ways Session can notify you of new messages.</string>
<string name="activity_pn_mode_fast_mode">Fast Mode</string>
<string name="activity_pn_mode_slow_mode">Slow Mode</string>
<string name="activity_pn_mode_fast_mode_explanation">Youll be notified of new messages reliably and immediately using Googles notification servers.</string>
<string name="activity_pn_mode_slow_mode_explanation">Session will occasionally check for new messages in the background.</string>
<string name="fragment_recovery_phrase_title">Recovery Phrase</string>
<string name="activity_prompt_passphrase_session_locked">Session is Locked</string>
<string name="activity_prompt_passphrase_tap_to_unlock">Tap to Unlock</string>
<string name="fragment_user_details_bottom_sheet_edit_text_hint">Enter a nickname</string>
</resources> </resources>

View File

@ -137,7 +137,7 @@
<style name="SessionIDTextView"> <style name="SessionIDTextView">
<item name="android:background">@drawable/session_id_text_view_background</item> <item name="android:background">@drawable/session_id_text_view_background</item>
<item name="android:padding">@dimen/medium_spacing</item> <item name="android:padding">@dimen/medium_spacing</item>
<item name="android:textSize">@dimen/large_font_size</item> <item name="android:textSize">20sp</item>
<item name="android:textColor">@color/text</item> <item name="android:textColor">@color/text</item>
<item name="android:fontFamily">@font/space_mono_regular</item> <item name="android:fontFamily">@font/space_mono_regular</item>
<item name="android:textAlignment">viewStart</item> <item name="android:textAlignment">viewStart</item>

View File

@ -140,6 +140,8 @@
</style> </style>
<style name="Theme.Session.BottomSheet" parent="@style/Theme.AppCompat.DayNight.Dialog"> <style name="Theme.Session.BottomSheet" parent="@style/Theme.AppCompat.DayNight.Dialog">
<item name="colorControlNormal">?android:textColorPrimary</item>
<item name="android:textColorPrimary">@color/text</item>
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.MaterialComponents.BottomSheetDialog</item> <item name="android:windowAnimationStyle">@style/Animation.MaterialComponents.BottomSheetDialog</item>
<item name="bottomSheetStyle">@style/Widget.Session.BottomSheetDialog</item> <item name="bottomSheetStyle">@style/Widget.Session.BottomSheetDialog</item>

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Play Icon --> <!-- Play Icon -->
<string name="play_icon_upper_path_data">M 44 32 L 44 64 L 100 64 L 100 64 Z</string> <string name="play_icon_upper_path_data" translatable="false">M 44 32 L 44 64 L 100 64 L 100 64 Z</string>
<string name="play_icon_bottom_path_data">M 44 96 L 44 64 L 100 64 L 100 64 Z</string> <string name="play_icon_bottom_path_data" translatable="false">M 44 96 L 44 64 L 100 64 L 100 64 Z</string>
<!-- Pause Icon --> <!-- Pause Icon -->
<string name="pause_icon_upper_path_data">M 32 40 L 32 56 L 96 56 L 96 40 Z</string> <string name="pause_icon_upper_path_data" translatable="false">M 32 40 L 32 56 L 96 56 L 96 40 Z</string>
<string name="pause_icon_bottom_path_data">M 32 88 L 32 72 L 96 72 L 96 88 Z</string> <string name="pause_icon_bottom_path_data" translatable="false">M 32 88 L 32 72 L 96 72 L 96 88 Z</string>
<!-- Groups --> <!-- Groups -->
<string name="play_icon_group_top">upperpart</string> <string name="play_icon_group_top" translatable="false">upperpart</string>
<string name="play_icon_group_bottom">bottompart</string> <string name="play_icon_group_bottom" translatable="false">bottompart</string>
<string name="play_icon_group_parts">parts</string> <string name="play_icon_group_parts" translatable="false">parts</string>
<!-- Path Name --> <!-- Path Name -->
<string name="play_icon_top_path_name">upper</string> <string name="play_icon_top_path_name" translatable="false">upper</string>
<string name="play_icon_bottom_path_name">bottom</string> <string name="play_icon_bottom_path_name" translatable="false">bottom</string>
</resources> </resources>

View File

@ -2,6 +2,7 @@ package org.session.libsession.database
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.AttachmentUploadJob
import org.session.libsession.messaging.jobs.Job import org.session.libsession.messaging.jobs.Job
import org.session.libsession.messaging.jobs.MessageSendJob import org.session.libsession.messaging.jobs.MessageSendJob
@ -31,11 +32,7 @@ interface StorageProtocol {
fun getUserDisplayName(): String? fun getUserDisplayName(): String?
fun getUserProfileKey(): ByteArray? fun getUserProfileKey(): ByteArray?
fun getUserProfilePictureURL(): String? fun getUserProfilePictureURL(): String?
fun setUserProfilePictureURL(newValue: String) fun setUserProfilePictureURL(newProfilePicture: String)
fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray?
fun getDisplayNameForRecipient(recipientPublicKey: String): String?
fun setProfileKeyForRecipient(recipientPublicKey: String, profileKey: ByteArray)
// Signal // Signal
fun getOrGenerateRegistrationID(): Int fun getOrGenerateRegistrationID(): Int
@ -133,9 +130,9 @@ interface StorageProtocol {
fun getLastUpdated(threadID: Long): Long fun getLastUpdated(threadID: Long): Long
// Contacts // Contacts
fun getDisplayName(publicKey: String): String? fun getContactWithSessionID(sessionID: String): Contact?
fun setDisplayName(publicKey: String, newName: String) fun getAllContacts(): Set<Contact>
fun getProfilePictureURL(publicKey: String): String? fun setContact(contact: Contact)
fun getRecipientSettings(address: Address): RecipientSettings? fun getRecipientSettings(address: Address): RecipientSettings?
fun addContacts(contacts: List<ConfigurationMessage.Contact>) fun addContacts(contacts: List<ConfigurationMessage.Contact>)

View File

@ -0,0 +1,87 @@
package org.session.libsession.messaging.contacts
import org.session.libsession.utilities.recipients.Recipient
class Contact(val sessionID: String) {
/**
* The URL from which to fetch the contact's profile picture.
*/
var profilePictureURL: String? = null
/**
* The file name of the contact's profile picture on local storage.
*/
var profilePictureFileName: String? = null
/**
* The key with which the profile picture is encrypted.
*/
var profilePictureEncryptionKey: ByteArray? = null
/**
* The ID of the thread associated with this contact.
*/
var threadID: Long? = null
/**
* This flag is used to determine whether we should auto-download files sent by this contact.
*/
var isTrusted = false
// region Name
/**
* The name of the contact. Use this whenever you need the "real", underlying name of a user (e.g. when sending a message).
*/
var name: String? = null
/**
* The contact's nickname, if the user set one.
*/
var nickname: String? = null
/**
* The name to display in the UI. For local use only.
*/
fun displayName(context: ContactContext): String? {
this.nickname?.let { return it }
return when (context) {
ContactContext.REGULAR -> this.name
ContactContext.OPEN_GROUP -> {
// In open groups, where it's more likely that multiple users have the same name,
// we display a bit of the Session ID after a user's display name for added context.
this.name?.let {
return "${this.name} (...${this.sessionID.takeLast(8)})"
}
return null
}
}
}
// endregion
enum class ContactContext {
REGULAR, OPEN_GROUP
}
fun isValid(): Boolean {
if (profilePictureURL != null) { return profilePictureEncryptionKey != null }
if (profilePictureEncryptionKey != null) { return profilePictureURL != null}
return true
}
override fun equals(other: Any?): Boolean {
return this.sessionID == (other as? Contact)?.sessionID
}
override fun hashCode(): Int {
return sessionID.hashCode()
}
override fun toString(): String {
return nickname ?: name ?: sessionID
}
companion object {
fun contextForRecipient(recipient: Recipient): ContactContext {
return if (recipient.isOpenGroupRecipient) {
ContactContext.OPEN_GROUP
} else {
ContactContext.REGULAR
}
}
}
}

View File

@ -1,22 +1,11 @@
package org.session.libsession.messaging.mentions package org.session.libsession.messaging.mentions
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.contacts.Contact
import org.session.libsignal.database.LokiUserDatabaseProtocol object MentionsManager {
class MentionsManager(private val userPublicKey: String, private val userDatabase: LokiUserDatabaseProtocol) {
var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys
companion object {
public lateinit var shared: MentionsManager
public fun configureIfNeeded(userPublicKey: String, userDatabase: LokiUserDatabaseProtocol) {
if (::shared.isInitialized) { return; }
shared = MentionsManager(userPublicKey, userDatabase)
}
}
fun cache(publicKey: String, threadID: Long) { fun cache(publicKey: String, threadID: Long) {
val cache = userPublicKeyCache[threadID] val cache = userPublicKeyCache[threadID]
if (cache != null) { if (cache != null) {
@ -26,14 +15,16 @@ class MentionsManager(private val userPublicKey: String, private val userDatabas
} }
} }
fun getMentionCandidates(query: String, threadID: Long): List<Mention> { fun getMentionCandidates(query: String, threadID: Long, isOpenGroup: Boolean): List<Mention> {
// Prepare
val cache = userPublicKeyCache[threadID] ?: return listOf() val cache = userPublicKeyCache[threadID] ?: return listOf()
// Prepare
val context = if (isOpenGroup) Contact.ContactContext.OPEN_GROUP else Contact.ContactContext.REGULAR
val storage = MessagingModuleConfiguration.shared.storage
val userPublicKey = storage.getUserPublicKey()
// Gather candidates // Gather candidates
var candidates: List<Mention> = cache.mapNotNull { publicKey -> var candidates: List<Mention> = cache.mapNotNull { publicKey ->
val displayName = userDatabase.getDisplayName(publicKey) val contact = storage.getContactWithSessionID(publicKey)
if (displayName == null) { return@mapNotNull null } val displayName = contact?.displayName(context) ?: return@mapNotNull null
if (displayName.startsWith("Anonymous")) { return@mapNotNull null }
Mention(publicKey, displayName) Mention(publicKey, displayName)
} }
candidates = candidates.filter { it.publicKey != userPublicKey } candidates = candidates.filter { it.publicKey != userPublicKey }

View File

@ -128,16 +128,20 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) {
if (allV2OpenGroups.contains(openGroup)) continue if (allV2OpenGroups.contains(openGroup)) continue
storage.addOpenGroup(openGroup) storage.addOpenGroup(openGroup)
} }
val profileManager = SSKEnvironment.shared.profileManager
val recipient = Recipient.from(context, Address.fromSerialized(userPublicKey), false)
if (message.displayName.isNotEmpty()) { if (message.displayName.isNotEmpty()) {
TextSecurePreferences.setProfileName(context, message.displayName) TextSecurePreferences.setProfileName(context, message.displayName)
storage.setDisplayName(userPublicKey, message.displayName) profileManager.setName(context, recipient, message.displayName)
} }
if (message.profileKey.isNotEmpty() && !message.profilePicture.isNullOrEmpty() if (message.profileKey.isNotEmpty() && !message.profilePicture.isNullOrEmpty()
&& TextSecurePreferences.getProfilePictureURL(context) != message.profilePicture) { && TextSecurePreferences.getProfilePictureURL(context) != message.profilePicture) {
val profileKey = Base64.encodeBytes(message.profileKey) val profileKey = Base64.encodeBytes(message.profileKey)
ProfileKeyUtil.setEncodedProfileKey(context, profileKey) ProfileKeyUtil.setEncodedProfileKey(context, profileKey)
storage.setProfileKeyForRecipient(userPublicKey, message.profileKey) profileManager.setProfileKey(context, recipient, message.profileKey)
storage.setUserProfilePictureURL(message.profilePicture!!) if (!message.profilePicture.isNullOrEmpty() && TextSecurePreferences.getProfilePictureURL(context) != message.profilePicture) {
storage.setUserProfilePictureURL(message.profilePicture!!)
}
} }
storage.addContacts(message.contacts) storage.addContacts(message.contacts)
} }
@ -162,9 +166,9 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
if (profile != null && userPublicKey != message.sender) { if (profile != null && userPublicKey != message.sender) {
val profileManager = SSKEnvironment.shared.profileManager val profileManager = SSKEnvironment.shared.profileManager
val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false) val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false)
val displayName = profile.displayName!! val name = profile.displayName!!
if (displayName.isNotEmpty()) { if (name.isNotEmpty()) {
profileManager.setDisplayName(context, recipient, displayName) profileManager.setName(context, recipient, name)
} }
if (profile.profileKey?.isNotEmpty() == true && profile.profilePictureURL?.isNotEmpty() == true if (profile.profileKey?.isNotEmpty() == true && profile.profilePictureURL?.isNotEmpty() == true
&& (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profile.profileKey))) { && (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profile.profileKey))) {

View File

@ -3,6 +3,7 @@ package org.session.libsession.messaging.utilities
import android.content.Context import android.content.Context
import org.session.libsession.R import org.session.libsession.R
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
import org.session.libsession.utilities.ExpirationUtil import org.session.libsession.utilities.ExpirationUtil
@ -12,8 +13,9 @@ object UpdateMessageBuilder {
var message = "" var message = ""
val updateData = updateMessageData.kind ?: return message val updateData = updateMessageData.kind ?: return message
if (!isOutgoing && sender == null) return message if (!isOutgoing && sender == null) return message
val storage = MessagingModuleConfiguration.shared.storage
val senderName: String = if (!isOutgoing) { val senderName: String = if (!isOutgoing) {
MessagingModuleConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender storage.getContactWithSessionID(sender!!)?.displayName(Contact.ContactContext.REGULAR) ?: sender
} else { context.getString(R.string.MessageRecord_you) } } else { context.getString(R.string.MessageRecord_you) }
when (updateData) { when (updateData) {
@ -33,7 +35,7 @@ object UpdateMessageBuilder {
} }
is UpdateMessageData.Kind.GroupMemberAdded -> { is UpdateMessageData.Kind.GroupMemberAdded -> {
val members = updateData.updatedMembers.joinToString(", ") { val members = updateData.updatedMembers.joinToString(", ") {
MessagingModuleConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it storage.getContactWithSessionID(it)?.displayName(Contact.ContactContext.REGULAR) ?: it
} }
message = if (isOutgoing) { message = if (isOutgoing) {
context.getString(R.string.MessageRecord_you_added_s_to_the_group, members) context.getString(R.string.MessageRecord_you_added_s_to_the_group, members)
@ -54,7 +56,7 @@ object UpdateMessageBuilder {
} else { } else {
// 2nd case: you are not part of the removed members // 2nd case: you are not part of the removed members
val members = updateData.updatedMembers.joinToString(", ") { val members = updateData.updatedMembers.joinToString(", ") {
storage.getDisplayNameForRecipient(it) ?: it storage.getContactWithSessionID(it)?.displayName(Contact.ContactContext.REGULAR) ?: it
} }
if (isOutgoing) { if (isOutgoing) {
context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members) context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members)
@ -76,8 +78,9 @@ object UpdateMessageBuilder {
fun buildExpirationTimerMessage(context: Context, duration: Long, sender: String? = null, isOutgoing: Boolean = false): String { fun buildExpirationTimerMessage(context: Context, duration: Long, sender: String? = null, isOutgoing: Boolean = false): String {
if (!isOutgoing && sender == null) return "" if (!isOutgoing && sender == null) return ""
val storage = MessagingModuleConfiguration.shared.storage
val senderName: String? = if (!isOutgoing) { val senderName: String? = if (!isOutgoing) {
MessagingModuleConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender storage.getContactWithSessionID(sender!!)?.displayName(Contact.ContactContext.REGULAR) ?: sender
} else { context.getString(R.string.MessageRecord_you) } } else { context.getString(R.string.MessageRecord_you) }
return if (duration <= 0) { return if (duration <= 0) {
if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages) if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages)
@ -90,7 +93,8 @@ object UpdateMessageBuilder {
} }
fun buildDataExtractionMessage(context: Context, kind: DataExtractionNotificationInfoMessage.Kind, sender: String? = null): String { fun buildDataExtractionMessage(context: Context, kind: DataExtractionNotificationInfoMessage.Kind, sender: String? = null): String {
val senderName = MessagingModuleConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender val storage = MessagingModuleConfiguration.shared.storage
val senderName = storage.getContactWithSessionID(sender!!)?.displayName(Contact.ContactContext.REGULAR) ?: sender!!
return when (kind) { return when (kind) {
DataExtractionNotificationInfoMessage.Kind.SCREENSHOT -> DataExtractionNotificationInfoMessage.Kind.SCREENSHOT ->
context.getString(R.string.MessageRecord_s_took_a_screenshot, senderName) context.getString(R.string.MessageRecord_s_took_a_screenshot, senderName)

View File

@ -29,7 +29,8 @@ class SSKEnvironment(
const val NAME_PADDED_LENGTH = 26 const val NAME_PADDED_LENGTH = 26
} }
fun setDisplayName(context: Context, recipient: Recipient, displayName: String) fun setNickname(context: Context, recipient: Recipient, nickname: String?)
fun setName(context: Context, recipient: Recipient, name: String)
fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String) fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String)
fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray) fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray)
fun setUnidentifiedAccessMode(context: Context, recipient: Recipient, unidentifiedAccessMode: Recipient.UnidentifiedAccessMode) fun setUnidentifiedAccessMode(context: Context, recipient: Recipient, unidentifiedAccessMode: Recipient.UnidentifiedAccessMode)

View File

@ -769,12 +769,12 @@ object TextSecurePreferences {
fun shouldUpdateProfile(context: Context, profileUpdateTime: Long) = fun shouldUpdateProfile(context: Context, profileUpdateTime: Long) =
profileUpdateTime > getLongPreference(context, LAST_PROFILE_UPDATE_TIME, 0) profileUpdateTime > getLongPreference(context, LAST_PROFILE_UPDATE_TIME, 0)
fun hasSeenFileServerInstabilityNotification(context: Context): Boolean { fun hasPerformedContactMigration(context: Context): Boolean {
return getBooleanPreference(context, "has_seen_file_server_instability_notification", false) return getBooleanPreference(context, "has_performed_contact_migration", false)
} }
fun setHasSeenFileServerInstabilityNotification(context: Context) { fun setPerformedContactMigration(context: Context) {
setBooleanPreference(context, "has_seen_file_server_instability_notification", true) setBooleanPreference(context, "has_performed_contact_migration", true)
} }
fun getLastTimeSessionOpened(context: Context): Long { fun getLastTimeSessionOpened(context: Context): Long {

View File

@ -26,10 +26,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.annimon.stream.function.Consumer; import com.annimon.stream.function.Consumer;
import com.esotericsoftware.kryo.util.Null;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.session.libsession.database.StorageProtocol;
import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.messaging.MessagingModuleConfiguration;
import org.session.libsession.avatars.TransparentContactPhoto; import org.session.libsession.avatars.TransparentContactPhoto;
import org.session.libsession.messaging.contacts.Contact;
import org.session.libsession.utilities.Address; import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.GroupRecord; import org.session.libsession.utilities.GroupRecord;
import org.session.libsession.utilities.recipients.RecipientProvider.RecipientDetails; import org.session.libsession.utilities.recipients.RecipientProvider.RecipientDetails;
@ -286,20 +289,23 @@ public class Recipient implements RecipientModifiedListener {
} }
public synchronized @Nullable String getName() { public synchronized @Nullable String getName() {
String displayName = MessagingModuleConfiguration.shared.getStorage().getDisplayName(this.address.toString()); StorageProtocol storage = MessagingModuleConfiguration.shared.getStorage();
if (displayName != null) { return displayName; } String sessionID = this.address.toString();
if (isGroupRecipient()) {
if (this.name == null && isGroupRecipient()) { if (this.name == null) {
List<String> names = new LinkedList<>(); List<String> names = new LinkedList<>();
for (Recipient recipient : participants) {
for (Recipient recipient : participants) { names.add(recipient.toShortString());
names.add(recipient.toShortString()); }
return Util.join(names, ", ");
} else {
return this.name;
} }
} else {
return Util.join(names, ", "); Contact contact = storage.getContactWithSessionID(sessionID);
if (contact == null) { return sessionID; }
return contact.displayName(Contact.ContactContext.REGULAR);
} }
return this.name;
} }
public void setName(@Nullable String name) { public void setName(@Nullable String name) {

View File

@ -1,441 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Session -->
<style name="Widget.Session.ActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid">
<item name="android:background">?colorPrimary</item>
<item name="elevation">1dp</item>
<item name="titleTextStyle">@style/TextAppearance.Session.DarkActionBar.TitleTextStyle</item>
</style>
<style name="Widget.Session.ActionBar.Flat">
<item name="android:elevation">0dp</item>
<item name="elevation">0dp</item>
</style>
<style name="TextAppearance.Session.DarkActionBar.TitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:textSize">@dimen/very_large_font_size</item>
</style>
<style name="Widget.Session.SearchView" parent="@style/Widget.AppCompat.SearchView">
<item name="closeIcon">@drawable/ic_clear</item>
</style>
<style name="ThemeOverlay.Session.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert">
<item name="buttonBarNegativeButtonStyle">@style/Widget.Session.AlertDialog.NegativeButtonStyle</item>
<item name="buttonBarPositiveButtonStyle">@style/Widget.Session.AlertDialog.PositiveButtonStyle</item>
</style>
<style name="Widget.Session.BottomSheetDialog" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/default_bottom_sheet_background</item>
</style>
<style name="Widget.Session.AlertDialog.NegativeButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">@color/accent</item>
</style>
<style name="Widget.Session.AlertDialog.PositiveButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">@color/accent</item>
</style>
<style name="Widget.Session.AppBarLayout" parent="@style/Widget.Design.AppBarLayout">
</style>
<style name="Widget.Session.TabBar" parent="Widget.AppCompat.ActionBar.TabBar">
<item name="elevation">1dp</item>
</style>
<style name="Widget.Session.TabLayout" parent="Widget.Design.TabLayout">
<item name="elevation">1dp</item>
<item name="tabIndicatorColor">?colorAccent</item>
<item name="tabSelectedTextColor">?colorAccent</item>>
<item name="tabIndicatorHeight">@dimen/accent_line_thickness</item>
<item name="tabRippleColor">@color/cell_selected</item>
<item name="tabTextAppearance">@style/TextAppearance.Session.Tab</item>
</style>
<style name="TextAppearance.Session.Tab" parent="TextAppearance.Design.Tab">
<item name="android:textSize">@dimen/medium_font_size</item>
<item name="textAllCaps">false</item>
</style>
<!-- TODO These button styles require proper background selectors for up/down visual state. -->
<style name="Widget.Session.Button.Common" parent="">
<item name="android:textAllCaps">false</item>
<item name="android:textSize">@dimen/medium_font_size</item>
<item name="android:fontFamily">sans-serif-medium</item>
<!-- Helps to get rid of nasty elevation. We want a flat style here. -->
<!-- https://stackoverflow.com/a/31003693/3802890 -->
<item name ="android:stateListAnimator">@null</item>
</style>
<style name="Widget.Session.Button.Common.ProminentFilled">
<item name="android:background">@drawable/prominent_filled_button_medium_background</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:drawableTint" tools:ignore="NewApi">?android:textColorPrimary</item>
</style>
<style name="Widget.Session.Button.Common.ProminentOutline">
<item name="android:background">@drawable/prominent_outline_button_medium_background</item>
<item name="android:textColor">@color/accent</item>
<item name="android:drawableTint" tools:ignore="NewApi">@color/accent</item>
</style>
<style name="Widget.Session.Button.Common.UnimportantFilled">
<item name="android:background">@drawable/unimportant_filled_button_medium_background</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:drawableTint" tools:ignore="NewApi">?android:textColorPrimary</item>
</style>
<style name="Widget.Session.Button.Common.UnimportantOutline">
<item name="android:background">@drawable/unimportant_outline_button_medium_background</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:drawableTint" tools:ignore="NewApi">?android:textColorPrimary</item>
</style>
<style name="Widget.Session.Button.Dialog" parent="">
<item name="android:textAllCaps">false</item>
<item name="android:textSize">@dimen/small_font_size</item>
<item name="android:textColor">?android:textColorPrimary</item>
</style>
<style name="Widget.Session.Button.Dialog.Unimportant">
<item name="android:background">@drawable/unimportant_dialog_button_background</item>
</style>
<style name="Widget.Session.Button.Dialog.Prominent">
<item name="android:background">@drawable/prominent_dialog_button_background</item>
<item name="android:textColor">#222</item>
</style>
<style name="Widget.Session.Button.Dialog.Destructive">
<item name="android:background">@drawable/destructive_dialog_button_background</item>
<item name="android:textColor">@android:color/white</item>
</style>
<style name="Widget.Session.EditText.Compose" parent="@style/Signal.Text.Body">
<item name="android:padding">2dp</item>
<item name="android:background">@null</item>
<item name="android:maxLines">4</item>
<item name="android:maxLength">65536</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:capitalize">sentences</item>
<item name="android:autoText">true</item>
<item name="android:gravity">center_vertical</item>
<item name="android:imeOptions">flagNoEnterAction</item>
<item name="android:inputType">textAutoCorrect|textCapSentences|textMultiLine</item>
<item name="android:contentDescription">@string/conversation_activity__compose_description</item>
<item name="android:textColorHint">?android:textColorHint</item>
<item name="android:textSize">@dimen/small_font_size</item>
<item name="android:textCursorDrawable">@drawable/session_edit_text_cursor</item>
<item name="android:textAlignment">viewStart</item>
</style>
<style name="ThemeOverlay.Session.Settings" parent="PreferenceThemeOverlay.v14.Material" >
<item name="android:textColor">@color/text</item>
<item name="android:textColorSecondary">#99FFFFFF</item>
<item name="android:textSize">@dimen/medium_font_size</item>
</style>
<style name="SessionIDTextView">
<item name="android:background">@drawable/session_id_text_view_background</item>
<item name="android:padding">@dimen/medium_spacing</item>
<item name="android:textSize">@dimen/large_font_size</item>
<item name="android:textColor">@color/text</item>
<item name="android:fontFamily">@font/space_mono_regular</item>
<item name="android:textAlignment">viewStart</item>
</style>
<style name="SessionEditText">
<item name="android:background">@drawable/session_id_text_view_background</item>
<item name="android:paddingLeft">@dimen/medium_spacing</item>
<item name="android:paddingTop">30dp</item>
<item name="android:paddingRight">@dimen/medium_spacing</item>
<item name="android:paddingBottom">30dp</item>
<item name="android:textSize">@dimen/small_font_size</item>
<item name="android:textColor">@color/text</item>
<item name="android:textCursorDrawable">@drawable/session_edit_text_cursor</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:maxLines">1</item>
</style>
<style name="SmallSessionEditText">
<item name="android:background">@drawable/session_id_text_view_background</item>
<item name="android:paddingLeft">@dimen/medium_spacing</item>
<item name="android:paddingTop">@dimen/medium_spacing</item>
<item name="android:paddingRight">@dimen/medium_spacing</item>
<item name="android:paddingBottom">@dimen/medium_spacing</item>
<item name="android:textSize">@dimen/small_font_size</item>
<item name="android:textColor">@color/text</item>
<item name="android:textCursorDrawable">@drawable/session_edit_text_cursor</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:maxLines">1</item>
</style>
<style name="FakeChatViewMessageBubble">
<item name="android:paddingLeft">@dimen/medium_spacing</item>
<item name="android:paddingTop">12dp</item>
<item name="android:paddingRight">@dimen/medium_spacing</item>
<item name="android:paddingBottom">12dp</item>
<item name="android:textSize">15sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:elevation">0dp</item>
<item name="android:textColor">?android:textColorPrimary</item>
</style>
<style name="FakeChatViewMessageBubble.Incoming">
<item name="android:background">@drawable/fake_chat_view_incoming_message_background</item>
<item name="android:elevation">10dp</item>
</style>
<style name="FakeChatViewMessageBubble.Outgoing">
<item name="android:background">@drawable/fake_chat_view_outgoing_message_background</item>
<item name="android:elevation">10dp</item>
</style>
<!-- Session -->
<style name="NoAnimation.Theme.BlackScreen" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowBackground">@android:color/black</item>
</style>
<style name="NoAnimation.Theme.AppCompat.Light.DarkActionBar" parent="@style/Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowAnimationStyle">@null</item>
</style>
<style name="TextSecure.DialogActivity" parent="Theme.AppCompat.Light">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
<style name="AppCompatAlertDialogStyleLight" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/signal_primary_dark</item>
</style>
<style name="AppCompatAlertDialogStyleDark" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">@color/signal_primary</item>
<item name="android:textColor">@null</item>
</style>
<style name="AppCompatDialogStyleLight" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/signal_primary_dark</item>
<item name="android:windowBackground">@drawable/dialog_background</item>
</style>
<style name="AppCompatDialogStyleDark" parent="Theme.AppCompat.Dialog">
<item name="colorAccent">@color/signal_primary</item>
<item name="android:windowBackground">@drawable/dialog_background</item>
<item name="android:textColor">@null</item>
</style>
<!-- ActionBar styles -->
<style name="TextSecure.DarkActionBar"
parent="@style/Widget.AppCompat.ActionBar">
<item name="background">@color/core_grey_90</item>
<item name="android:popupTheme" tools:ignore="NewApi">@style/ThemeOverlay.AppCompat.Dark</item>
<item name="elevation">1dp</item>
<item name="popupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
<item name="titleTextStyle">@style/TextSecure.TitleTextStyle</item>
<item name="subtitleTextStyle">@style/TextSecure.SubtitleTextStyle</item>
<item name="android:textColorSecondary">@color/white</item>
</style>
<style name="TextSecure.LightActionBar"
parent="@style/Widget.AppCompat.ActionBar">
<item name="background">@color/textsecure_primary</item>
<item name="elevation">1dp</item>
<item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>
<item name="titleTextStyle">@style/TextSecure.TitleTextStyle</item>
<item name="subtitleTextStyle">@style/TextSecure.SubtitleTextStyle</item>
<item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">@color/white</item>
</style>
<style name="TextSecure.LightActionBar.DarkText"
parent="TextSecure.LightActionBar">
<item name="android:textColorPrimary">@color/black</item>
<item name="android:textColorSecondary">@color/black</item>
</style>
<style name="TextSecure.FlatLightActionBar"
parent="@style/TextSecure.LightActionBar">
<item name="elevation">0dp</item>
</style>
<style name="TextSecure.DarkActionBar.TabBar"
parent="@style/Widget.AppCompat.ActionBar.TabBar">
<item name="background">@color/gray95</item>
<item name="android:background">@color/gray95</item>
<item name="elevation">4dp</item>
</style>
<style name="TextSecure.LightActionBar.TabBar"
parent="@style/Widget.AppCompat.ActionBar.TabBar">
<item name="android:background">@color/textsecure_primary</item>
<item name="background">@color/textsecure_primary</item>
<item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">#BFffffff</item>
<item name="elevation">4dp</item>
</style>
<style name="TextSecure.TitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
<item name="android:textColor">@color/white</item>
<item name="android:textColorHint">#BFffffff</item>
</style>
<style name="TextSecure.SubtitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Subtitle">
<item name="android:textColor">#BFffffff</item>
</style>
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textStyle">bold</item>
</style>
<style name="Registration.Description" parent="@android:style/TextAppearance">
<item name="android:textSize">16.0sp</item>
<item name="android:typeface">sans</item>
<item name="android:textStyle">normal</item>
<item name="android:gravity">left</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:shadowColor">#ffffff</item>
<item name="android:shadowDx">1.0</item>
<item name="android:shadowDy">1.0</item>
<item name="android:shadowRadius">0.0</item>
<item name="android:lineSpacingMultiplier">1.25</item>
</style>
<style name="Registration.Label" parent="@android:style/TextAppearance">
<item name="android:textSize">12.0sp</item>
<item name="android:typeface">sans</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">#ff808080</item>
<item name="android:gravity">left</item>
<item name="android:layout_gravity">left</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:shadowColor">#ffffff</item>
<item name="android:shadowDx">1.0</item>
<item name="android:shadowDy">1.0</item>
<item name="android:shadowRadius">0.0</item>
<item name="android:lineSpacingMultiplier">1.25</item>
</style>
<style name="Registration.BigLabel" parent="@style/Registration.Label">
<item name="android:textSize">20sp</item>
</style>
<style name="Registration.Constant" parent="@android:style/TextAppearance">
<item name="android:typeface">sans</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">#ff808080</item>
<item name="android:shadowColor">#ffffff</item>
<item name="android:shadowDx">1.0</item>
<item name="android:shadowDy">1.0</item>
<item name="android:shadowRadius">0.0</item>
<item name="android:lineSpacingMultiplier">1.25</item>
</style>
<!-- For Holo Light Dialog Activity Styling Emulation -->
<style name="Widget.ProgressBar.Horizontal" parent="@android:style/Widget.Holo.ProgressBar.Horizontal">
</style>
<style name="MaterialButton">
<item name="android:elevation" tools:ignore="NewApi">1dp</item>
<item name="android:translationZ" tools:ignore="NewApi">1dp</item>
<item name="android:textColor">@color/white</item>
<item name="android:textSize">12sp</item>
</style>
<style name="InfoButton" parent="@style/MaterialButton">
<item name="android:background">@drawable/info_round</item>
</style>
<style name="ErrorButton" parent="@style/MaterialButton">
<item name="android:background">@drawable/error_round</item>
</style>
<style name="AttachmentTypeLabel">
<item name="android:textColor">?android:textColorTertiary</item>
<item name="android:textSize">@dimen/small_font_size</item>
</style>
<style name="Button.Primary" parent="Base.Widget.AppCompat.Button.Colored">
<item name="colorAccent">@color/signal_primary</item>
<item name="android:textColor">@color/white</item>
</style>
<style name="Button.Borderless" parent="Base.Widget.AppCompat.Button.Borderless">
<item name="android:textColor">@color/signal_primary</item>
</style>
<style name="Button.Borderless.Registration" parent="Base.Widget.AppCompat.Button.Borderless">
<item name="android:textColor">@color/core_grey_60</item>
</style>
<!-- RedPhone -->
<!-- Buttons in the main "button row" of the in-call onscreen touch UI. -->
<!-- "Compound button" variation of InCallButton.
These buttons have the concept of two states: checked and unchecked.
(This style is just like "InCallButton" except that we also
clear out android:textOn and android:textOff, to avoid the default
text label behavior of the ToggleButton class.) -->
<style name="WebRtcCallCompoundButton">
<item name="android:layout_height">31dp</item>
<item name="android:layout_width">31dp</item>
<item name="android:textOn">@null</item>
<item name="android:textOff">@null</item>
</style>
<style name="IdentityKey">
<item name="android:fontFamily">monospace</item>
<item name="android:typeface">monospace</item>
<item name="android:textSize">17sp</item>
<item name="android:clickable">false</item>
<item name="android:focusable">false</item>
</style>
<style name="BackupPassphrase">
<item name="android:fontFamily">monospace</item>
<item name="android:typeface">monospace</item>
<item name="android:textSize">15sp</item>
<item name="android:clickable">false</item>
<item name="android:focusable">false</item>
</style>
<style name="PreferenceThemeOverlay.Fix" parent="PreferenceThemeOverlay.v14.Material">
</style>
<style name="Color1SwitchStyle">
<item name="colorControlActivated">@color/white</item>
</style>
<style name="BottomSheetActionItem">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/action_item_height</item>
<item name="android:textSize">@dimen/medium_font_size</item>
<item name="android:drawablePadding">@dimen/drawable_padding</item>
<item name="android:padding">@dimen/normal_padding</item>
<item name="android:gravity">center_vertical</item>
<item name="android:selectable">true</item>
<item name="android:foreground">?attr/selectableItemBackground</item>
</style>
<style name="StickerPopupAnimation" parent="@android:style/Animation">
<item name="android:windowEnterAnimation">@anim/fade_in</item>
<item name="android:windowExitAnimation">@anim/fade_out</item>
</style>
</resources>

View File

@ -1,294 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Session -->
<!-- Due to historical reasons the base theme is dark and the light one
is implemented using "notnight" type of resources. -->
<style name="Base.Theme.Session" parent="@style/Theme.AppCompat.DayNight.DarkActionBar">
<item name="colorPrimary">@color/action_bar_background</item>
<item name="colorPrimaryDark">@color/action_bar_background</item>
<item name="colorAccent">@color/accent</item>
<item name="colorControlNormal">?android:textColorPrimary</item>
<item name="colorControlActivated">?colorAccent</item>
<item name="colorControlHighlight">?colorAccent</item>
<item name="android:textColorPrimary">@color/text</item>
<item name="android:textColorSecondary">?android:textColorPrimary</item>
<item name="android:textColorTertiary">@color/unimportant</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:textColorHint">@color/gray27</item>
<item name="android:windowBackground">@drawable/default_session_background</item>
<item name="android:colorBackground">@color/default_background_start</item>
<item name="android:navigationBarColor">@color/compose_view_background</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.DayNight.ActionBar</item>
<item name="actionBarStyle">@style/Widget.Session.ActionBar</item>
<item name="alertDialogTheme">@style/ThemeOverlay.Session.AlertDialog</item>
<item name="bottomSheetDialogTheme">@style/Theme.Session.BottomSheet</item>
<item name="preferenceTheme">@style/ThemeOverlay.Session.Settings</item>
<item name="appBarLayoutStyle">@style/Widget.Session.AppBarLayout</item>
<item name="actionBarTabBarStyle">@style/Widget.Session.TabBar</item>
<item name="statusBarBackground">@color/accent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="windowActionModeOverlay">true</item>
<item name="actionModeCloseDrawable">@drawable/ic_baseline_clear_24</item>
<item name="actionModeBackground">@color/compose_view_background</item>
<item name="dividerVertical">@color/separator</item>
<item name="dividerHorizontal">?dividerVertical</item>
<item name="searchViewStyle">@style/Widget.Session.SearchView</item>
<!-- App specific attributes -->
<item name="ic_visibility_on">@drawable/ic_baseline_visibility_24</item>
<item name="ic_visibility_off">@drawable/ic_baseline_visibility_off_24</item>
<item name="ic_arrow_forward">@drawable/ic_baseline_arrow_forward_24</item>
<item name="dialog_background_color">@color/dialog_background</item>
<item name="media_overview_toolbar_background">@color/transparent</item>
<item name="media_overview_header_foreground">@color/text</item>
<item name="media_keyboard_button_color">@color/core_grey_25</item>\
<item name="attachment_type_selector_background">?android:windowBackground</item>
<item name="attachment_type_selector_hide_button_background">@color/gray50</item>
<item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item>
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
<item name="attachment_document_extension_text_color">#222</item>
<item name="home_gradient_start">#00000000</item>
<item name="home_gradient_end">#FF000000</item>
<item name="message_received_background_color">#222325</item>
<item name="message_sent_background_color">#3F4146</item>
<item name="menu_accept_icon">@drawable/ic_baseline_done_24</item>
<item name="menu_trash_icon">@drawable/ic_baseline_delete_24</item>
<item name="menu_block_icon">@drawable/ic_baseline_block_24</item>
<item name="menu_forward_icon">@drawable/ic_baseline_forward_24</item>
<item name="menu_save_icon">@drawable/ic_baseline_save_24</item>
<item name="menu_photo_library_icon">@drawable/ic_baseline_photo_library_24</item>
<item name="menu_delete_icon">@drawable/ic_baseline_delete_24</item>
<item name="menu_copy_icon">@drawable/ic_baseline_file_copy_24</item>
<item name="menu_reply_icon">@drawable/ic_baseline_reply_24</item>
<item name="menu_selectall_icon">@drawable/ic_baseline_select_all_24</item>
<item name="menu_split_icon">@drawable/ic_baseline_call_split_24</item>
<item name="menu_popup_expand">@drawable/ic_baseline_launch_24</item>
<item name="menu_info_icon">@drawable/ic_baseline_info_24</item>
<item name="conversation_emoji_toggle">@drawable/ic_emoji_filled_keyboard_24</item>
<item name="conversation_sticker_toggle">@drawable/ic_sticker_filled_keyboard_24</item>
<item name="conversation_keyboard_toggle">@drawable/ic_baseline_keyboard_24</item>
<item name="conversation_input_background">@drawable/compose_background_dark</item>
<item name="quick_camera_icon">@drawable/ic_baseline_photo_camera_24</item>
<item name="quick_mic_icon">@drawable/ic_baseline_mic_24</item>
<item name="conversation_item_audio_seek_bar_color_incoming">@color/accent</item>
<item name="conversation_item_audio_seek_bar_color_outgoing">@color/accent</item>
<item name="conversation_item_audio_seek_bar_background_color">@color/text</item>
</style>
<!-- This should be the default theme for the application. -->
<style name="Theme.Session.DayNight" parent="Base.Theme.Session">
<!-- leave empty to allow overriding -->
</style>
<style name="Theme.Session.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="Theme.Session.DayNight.FlatActionBar">
<item name="actionBarStyle">@style/Widget.Session.ActionBar.Flat</item>
</style>
<!--
This is a temporary theme that is used by any activity
which doesn't have support for light theme
(like some old Signal screens or third-party libs with white only icons)
-->
<!-- TODO Refactor this to use color resources -->
<style name="Base.Theme.Session.ForceDark" parent="Theme.Session.DayNight">
<item name="colorPrimary">#171717</item>
<item name="android:textColorPrimary">#FFFFFF</item>
<item name="android:textColorSecondary">#DFFFFFFF</item>
<item name="android:textColorTertiary">#90FFFFFF</item>
<item name="colorControlNormal">?android:textColorPrimary</item>
<item name="android:colorBackground">#121212</item>
<item name="android:windowBackground">?android:colorBackground</item>
<item name="android:navigationBarColor">?android:colorBackground</item>
<item name="android:statusBarColor">@color/transparent</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
<item name="actionBarStyle">@style/Widget.AppCompat.ActionBar</item>
<item name="conversation_emoji_toggle">@drawable/ic_emoji_filled_keyboard_24</item>
<item name="conversation_sticker_toggle">@drawable/ic_sticker_filled_keyboard_24</item>
<item name="conversation_keyboard_toggle">@drawable/ic_baseline_keyboard_24</item>
<item name="conversation_input_background">@drawable/compose_background_dark</item>
</style>
<style name="Theme.Session.ForceDark" parent="Base.Theme.Session.ForceDark">
<!-- leave empty to allow overriding -->
</style>
<style name="Theme.Session.ForceDark.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="Theme.Session.BottomSheet" parent="@style/Theme.AppCompat.DayNight.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.MaterialComponents.BottomSheetDialog</item>
<item name="bottomSheetStyle">@style/Widget.Session.BottomSheetDialog</item>
</style>
<!-- Session -->
<!-- Original Signal dark theme -->
<style name="Base.Theme.TextSecure" parent="@style/Theme.Session.DayNight">
<item name="windowActionModeOverlay">true</item>
<item name="colorPrimary">@color/action_bar_background</item>
<item name="colorPrimaryDark">@color/action_bar_background</item>
<item name="media_overview_toolbar_background">@color/transparent</item>
<item name="media_overview_header_foreground">@color/text</item>
<item name="theme_type">dark</item>
<item name="android:navigationBarColor">@color/compose_view_background</item>
<item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item>
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
<item name="conversation_list_item_background">@drawable/conversation_list_item_background_dark</item>
<item name="conversation_list_item_contact_color">#ffdddddd</item>
<item name="conversation_list_item_subject_color">#ffdddddd</item>
<item name="conversation_list_item_delivery_icon_color">@color/core_grey_25</item>
<item name="conversation_list_item_date_color">#ffdddddd</item>
<item name="conversation_list_item_unread_color">@color/core_white</item>
<item name="conversation_list_item_unread_background">@drawable/unread_count_background_dark</item>
<item name="conversation_list_item_divider">@drawable/conversation_list_divider_shape_dark</item>
<item name="conversation_list_toolbar_background">@color/action_bar_background</item>
<item name="conversation_list_typing_tint">@color/core_white</item>
<item name="conversation_group_member_name">#99ffffff</item>
<item name="conversation_item_bubble_background">?message_sent_background_color</item>
<item name="conversation_item_sent_text_primary_color">@color/core_grey_05</item>
<item name="conversation_item_sent_text_secondary_color">@color/core_grey_25</item>
<item name="conversation_item_sent_icon_color">@color/core_grey_25</item>
<item name="conversation_item_sent_text_indicator_tab_color">#99ffffff</item>
<item name="conversation_item_received_text_primary_color">@color/text</item>
<item name="conversation_item_received_text_secondary_color">@color/text</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape_dark</item>
<item name="conversation_item_sticky_date_background">@drawable/sticky_date_header_background_dark</item>
<item name="conversation_item_sticky_date_text_color">@color/core_grey_45</item>
<item name="conversation_item_image_outline_color">@color/transparent_white_30</item>
<item name="verification_background">@color/core_grey_95</item>
<item name="dialog_info_icon">@drawable/ic_info_outline_dark</item>
<item name="dialog_alert_icon">@drawable/ic_warning_dark</item>
<item name="device_link_item_card_background">@color/device_link_item_background_dark</item>
<item name="fab_color">@color/textsecure_primary_dark</item>
<item name="lower_right_divet">@drawable/divet_lower_right_light</item>
<item name="conversation_background">@color/loki_darkest_gray</item>
<item name="conversation_editor_background">#22ffffff</item>
<item name="conversation_editor_text_color">#ffeeeeee</item>
<item name="conversation_input_inline_attach_icon_tint">@color/core_grey_05</item>
<item name="conversation_transport_sms_indicator">@drawable/ic_arrow_up_circle_24</item>
<item name="conversation_transport_push_indicator">@drawable/ic_arrow_up_circle_24</item>
<item name="conversation_transport_popup_background">@color/black</item>
<item name="conversation_attach_camera">@drawable/ic_photo_camera_dark</item>
<item name="conversation_attach_image">@drawable/ic_image_dark</item>
<item name="conversation_attach_video">@drawable/ic_movie_creation_dark</item>
<item name="conversation_attach_sound">@drawable/ic_volume_up_dark</item>
<item name="conversation_attach_contact_info">@drawable/ic_account_box_dark</item>
<item name="conversation_attach">@drawable/ic_attach_white_24dp</item>
<item name="conversation_number_picker_text_color_normal">@color/gray13</item>
<item name="conversation_number_picker_text_color_selected">@color/white</item>
<item name="conversation_sticker_footer_text_color">@color/core_grey_25</item>
<item name="conversation_sticker_footer_icon_color">@color/core_grey_25</item>
<item name="conversation_sticker_author_color">@color/core_grey_05</item>
<item name="emoji_tab_strip_background">@color/compose_view_background</item>
<item name="emoji_tab_indicator">@color/accent</item>
<item name="emoji_tab_underline">@color/gray78</item>
<item name="emoji_tab_seperator">@color/gray70</item>
<item name="emoji_drawer_background">@color/compose_text_view_background</item>
<item name="emoji_text_color">@color/white</item>
<item name="emoji_category_recent">@drawable/ic_recent_dark_20</item>
<item name="emoji_category_people">@drawable/ic_emoji_people_dark_20</item>
<item name="emoji_category_nature">@drawable/ic_emoji_animal_dark_20</item>
<item name="emoji_category_foods">@drawable/ic_emoji_food_dark_20</item>
<item name="emoji_category_activity">@drawable/ic_emoji_activity_dark_20</item>
<item name="emoji_category_places">@drawable/ic_emoji_travel_dark_20</item>
<item name="emoji_category_objects">@drawable/ic_emoji_object_dark_20</item>
<item name="emoji_category_symbol">@drawable/ic_emoji_symbol_dark_20</item>
<item name="emoji_category_flags">@drawable/ic_emoji_flag_dark_20</item>
<item name="emoji_category_emoticons">@drawable/ic_emoji_emoticon_dark_20</item>
<item name="emoji_variation_selector_background">@drawable/emoji_variation_selector_background_dark</item>
<item name="linkpreview_background_color">@color/black</item>
<item name="linkpreview_primary_text_color">@color/text</item>
<item name="linkpreview_secondary_text_color">@color/text</item>
<item name="linkpreview_divider_color">@color/transparent</item>
<item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item>
<item name="conversation_icon_attach_video">@drawable/ic_video_dark</item>
<item name="sticker_management_icon">@drawable/sticker_button_dark</item>
<item name="sticker_management_divider_color">@color/core_grey_75</item>
<item name="sticker_management_empty_background_color">@color/core_grey_85</item>
<item name="sticker_management_action_button_color">@color/core_grey_25</item>
<item name="sticker_popup_background">@color/transparent_black_90</item>
<item name="sticker_preview_toolbar_background">@color/core_grey_95</item>
<item name="sticker_preview_status_bar_color">@color/core_grey_85</item>
<item name="sticker_view_missing_background">@drawable/sticker_missing_background_dark</item>
<item name="tooltip_default_color">@color/core_grey_75</item>
<item name="pref_icon_tint">#FFFFFF</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item>
<item name="shared_contact_details_header_background">@color/grey_800</item>
<item name="shared_contact_details_titlebar">@color/grey_900</item>
<item name="shared_contact_item_button_color">@color/core_grey_85</item>
</style>
<style name="Theme.TextSecure.DayNight" parent="Base.Theme.TextSecure">
<!-- leave empty to allow overriding -->
</style>
<style name="Theme.TextSecure.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="Theme.TextSecure.Dialog.Rationale" parent="Theme.AppCompat.DayNight.Dialog.Alert">
<item name="android:windowBackground">@drawable/default_dialog_background</item>
</style>
<style name="Theme.TextSecure.Dialog.MediaSendProgress" parent="@android:style/Theme.Dialog">
<item name="android:background">@drawable/default_dialog_background</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>

View File

@ -19,24 +19,14 @@ interface LokiAPIDatabaseProtocol {
fun setReceivedMessageHashValues(publicKey: String, newValue: Set<String>) fun setReceivedMessageHashValues(publicKey: String, newValue: Set<String>)
fun getAuthToken(server: String): String? fun getAuthToken(server: String): String?
fun setAuthToken(server: String, newValue: String?) fun setAuthToken(server: String, newValue: String?)
fun getLastMessageServerID(group: Long, server: String): Long?
fun setLastMessageServerID(group: Long, server: String, newValue: Long)
fun getLastDeletionServerID(group: Long, server: String): Long?
fun setLastDeletionServerID(group: Long, server: String, newValue: Long)
fun setUserCount(group: Long, server: String, newValue: Int) fun setUserCount(group: Long, server: String, newValue: Int)
fun setUserCount(room: String, server: String, newValue: Int)
fun getLastMessageServerID(room: String, server: String): Long? fun getLastMessageServerID(room: String, server: String): Long?
fun setLastMessageServerID(room: String, server: String, newValue: Long) fun setLastMessageServerID(room: String, server: String, newValue: Long)
fun getLastDeletionServerID(room: String, server: String): Long? fun getLastDeletionServerID(room: String, server: String): Long?
fun setLastDeletionServerID(room: String, server: String, newValue: Long) fun setLastDeletionServerID(room: String, server: String, newValue: Long)
fun setUserCount(room: String, server: String, newValue: Int)
fun getSessionRequestSentTimestamp(publicKey: String): Long?
fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long)
fun getSessionRequestProcessedTimestamp(publicKey: String): Long?
fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long)
fun getOpenGroupPublicKey(server: String): String? fun getOpenGroupPublicKey(server: String): String?
fun setOpenGroupPublicKey(server: String, newValue: String) fun setOpenGroupPublicKey(server: String, newValue: String)
fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String)
fun getOpenGroupProfilePictureURL(group: Long, server: String): String?
fun getLastSnodePoolRefreshDate(): Date? fun getLastSnodePoolRefreshDate(): Date?
fun setLastSnodePoolRefreshDate(newValue: Date) fun setLastSnodePoolRefreshDate(newValue: Date)
fun getUserX25519KeyPair(): ECKeyPair fun getUserX25519KeyPair(): ECKeyPair

View File

@ -2,6 +2,5 @@ package org.session.libsignal.database
interface LokiMessageDatabaseProtocol { interface LokiMessageDatabaseProtocol {
fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long?
fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) fun setServerID(messageID: Long, serverID: Long, isSms: Boolean)
} }

View File

@ -1,7 +0,0 @@
package org.session.libsignal.database
interface LokiUserDatabaseProtocol {
fun getDisplayName(publicKey: String): String?
fun getProfilePictureURL(publicKey: String): String?
}