mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
commit
b5b3f4aebe
@ -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"
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
@ -418,7 +416,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
Log.i(TAG, "onNewIntent()");
|
Log.i(TAG, "onNewIntent()");
|
||||||
|
|
||||||
if (isFinishing()) {
|
if (isFinishing()) {
|
||||||
Log.w(TAG, "Activity is finishing...");
|
Log.w(TAG, "Activity is finishing...");
|
||||||
return;
|
return;
|
||||||
@ -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
|
||||||
@ -2344,8 +2333,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
} 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(getResources().getString(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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +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.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
|
||||||
@ -79,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) {
|
||||||
@ -507,16 +491,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? {
|
||||||
|
@ -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();
|
||||||
|
@ -132,12 +132,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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 }
|
||||||
|
@ -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 ->
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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
|
||||||
@ -56,4 +57,8 @@ 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))
|
||||||
}
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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 + ": "));
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
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"
|
android:text="Conversation"
|
||||||
|
@ -16,25 +16,95 @@
|
|||||||
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" />
|
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
|
||||||
|
|
||||||
|
@ -813,8 +813,10 @@
|
|||||||
<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_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="fragment_user_details_bottom_sheet_edit_text_hint">Enter a nickname</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 }
|
||||||
|
@ -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))) {
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -764,12 +764,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)
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
@ -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) {
|
||||||
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
package org.session.libsignal.database
|
|
||||||
|
|
||||||
interface LokiUserDatabaseProtocol {
|
|
||||||
|
|
||||||
fun getDisplayName(publicKey: String): String?
|
|
||||||
fun getProfilePictureURL(publicKey: String): String?
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user