diff --git a/.tx/config b/.tx/config deleted file mode 100644 index 9661334afd..0000000000 --- a/.tx/config +++ /dev/null @@ -1,10 +0,0 @@ -[main] -host = https://www.transifex.com -lang_map = da_DK:da-rDK,he:iw,id:in,kn_IN:kn-rIN,pt_BR:pt-rBR,pt_PT:pt,qu_EC:qu-rEC,sv_SE:sv-rSE,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW - -[signal-android.master] -file_filter = app/src/main/res/values-/strings.xml -source_file = app/src/main/res/values/strings.xml -source_lang = en -type = ANDROID - diff --git a/BUILDING.md b/BUILDING.md index 5fa550b3f9..e78207d964 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -15,11 +15,11 @@ You will need Java 8 set up on your machine. Ensure that the following packages are installed from the Android SDK manager: * Android SDK Build Tools (see buildToolsVersion in build.gradle) -* SDK Platform (All API levels) +* SDK Platform (all API levels) * Android Support Repository * Google Repository -In Android studio, this can be done from the Quickstart panel, choose "Configure" then "SDK Manager". In the SDK Tools tab of the SDK Manager, make sure that the "Android Support Repository" is installed, and that the latest "Android SDK build-tools" are installed. Click "OK" to return to the Quickstart panel. You may also need to install API version 28 in the SDK platforms tab. +In Android studio, this can be done from the Quickstart panel. Just choose "Configure", then "SDK Manager". In the SDK Tools tab of the SDK Manager, make sure that "Android Support Repository" is installed, and that the latest "Android SDK build-tools" are installed. Click "OK" to return to the Quickstart panel. You may also need to install API version 28 in the SDK platforms tab. Setting up a development environment and building from Android Studio ------------------------------------ @@ -33,27 +33,7 @@ Setting up a development environment and building from Android Studio 5. Default config options should be good enough. 6. Project initialization and building should proceed. -Building Session from the command line ---------------- - -The following steps should help you (re)build Session from the command line once all dependencies have been acquired. - -1. Checkout the session-android project source with the command: - - git clone https://github.com/oxen-io/session-android.git - -2. Make sure you have the [Android SDK](https://developer.android.com/sdk/index.html) installed. -3. Create a local.properties file at the root of your source checkout and add an sdk.dir entry to it. For example: - - sdk.dir=/Application/android-sdk-macosx - -4. Execute Gradle: - - ./gradlew :app:build - Contributing code ----------------- -The bulk of the Session code can be found under src/org/thoughtcrime/securesms/loki on this repo and java/main/java/org/whispersystems/signalservice/loki on the session-android-service repo. - -Code contributions should be sent via github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests). +Code contributions should be sent via Github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests). diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 1408e552ad..0000000000 --- a/NOTICE +++ /dev/null @@ -1,34 +0,0 @@ -TextSecure provides encrypted text messages for Android. -Copyright 2011 Whisper Systems - -This software has the follow third party dependencies: - -Bouncy Castle 1.42 -http://www.bouncycastle.org/ -MIT License - -Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -ZXing -http://code.google.com/p/zxing/ -Apache License 2.0 - -Copyright 2009 ZXing authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/README.md b/README.md index 5e41159c45..ad35159e79 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Add the [F-Droid repo](https://fdroid.getsession.org/) Session integrates directly with [Oxen Service Nodes](https://docs.oxen.io/about-the-oxen-blockchain/oxen-service-nodes), which are a set of distributed, decentralized and Sybil resistant nodes. Service Nodes act as servers which store messages offline, and a set of nodes which allow for onion routing functionality obfuscating users' IP addresses. For a full understanding of how Session works, read the [Session Whitepaper](https://getsession.org/whitepaper). -![AndroidSession](https://i.imgur.com/0YC9TyI.png) + ## Want to contribute? Found a bug or have a feature request? @@ -20,12 +20,53 @@ Please search for any [existing issues](https://github.com/oxen-io/session-andro Build instructions can be found in [BUILDING.md](BUILDING.md). +## Translations + +Want to help us translate Session into your language? You can do so at https://crowdin.com/project/session-android! + +## Verifying signatures + +**Step 1:** + +``` +wget https://raw.githubusercontent.com/oxen-io/oxen-core/master/utils/gpg_keys/KeeJef.asc +gpg --import KeeJef.asc +``` + +**Step 2:** + +Get the signed hash for this release. `SESSION_VERSION` needs to be updated for the release you want to verify. + +``` +export SESSION_VERSION=1.10.4 +wget https://github.com/oxen-io/session-android/releases/download/$SESSION_VERSION/signatures.asc +``` + +**Step 3:** + +Verify the signature of the hashes of the files. + +``` +gpg --verify signatures.asc 2>&1 |grep "Good signature from" +``` + +The command above should print "`Good signature from "Kee Jefferys...`". If it does, the hashes are valid but we still have to make the sure the signed hashes matches the downloaded files. + +**Step 4:** + +Make sure the two commands below returns the same hash. If they do, files are valid. + +``` +sha256sum session-$SESSION_VERSION-universal.apk +grep universal.apk signatures.asc +``` + ## License Copyright 2011 Whisper Systems Copyright 2013-2017 Open Whisper Systems -Copyright 2019-2020 The Loki Project +Copyright 2019-2021 The Loki Project Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html diff --git a/TRANSLATION.md b/TRANSLATION.md deleted file mode 100644 index ff32809c16..0000000000 --- a/TRANSLATION.md +++ /dev/null @@ -1,54 +0,0 @@ -## How to translate Session into new languages quickly and easily - -There are people all around the world who can benefit from access to Session — and that means we need to make Session available in as many languages as possible. We’ve had a number of requests from community members wanting to help out by translating Session into currently unsupported (or only partially supported) languages. If you speak multiple languages and you want to help us make Session available in another language you speak, this guide will show you how to translate Session’s iOS and Android apps quickly and easily. - -### Translating Session for iOS: - -Translating Session iOS into a new language is easy! You’ll need a web browser, a plaintext editor (TextEdit on macOS or Notepad on Windows will do), and a little bit of time and patience. - -#### Step 1: Retrieving English strings -- Go to [Session’s iOS GitHub page](https://github.com/loki-project/session-ios) -- In the list of folders and files, click **Session** -- Click **Meta** -- Click **Translations** -- Click **en.lproj** -- Click **localizable.strings** -- Scroll down to Line #2557 (using the line numbers on the left-hand side of the text). This line should contain the text `// MARK: - Session` (note: if this line does not contain that text, use Ctrl+F, Cmd+F, or your browser’s Find on Page function to find the line which contains that text) -- Select and copy all text from that line down to the end of the file -- Open a plaintext editor (TextEdit on macOS, Notepad on Windows, or your preferred plaintext editor) and create a new file -- Paste the text you copied earlier into this blank file - -#### Step 2: Translate! -This file will now have a large number of lines of code. Each line contains 2 sets of information, both in quotation marks, like this: - -`"continue_2" = "Continue";` - -For each line of code, translate the word or sentence to the **right** of the equals sign into the language you are translating into. Do **not** edit the text to the **left** of the equals sign. - -Once you are finished translating, save the file with a filename that specifies which language you have translated the app into, and email the file to us at support@getsession.org along with information about the language you have translated the app into. Be sure to specify in your email that you have translated the iOS app (for information on translating the Android app, see below). Our developers will then take your translation and apply it to the Session iOS app. Thank you for helping make Session more accessible for everyone! - - -### Translating for Android: - -It’s just as easy to add new translations on Session Android! Once again, you’ll need a web browser, a plaintext editor (TextEdit on macOS or Notepad on Windows will do), and a little bit of time and patience. - -#### Step 1: Retrieving English strings -- Go to [Session’s Android GitHub page](https://github.com/loki-project/session-android) -- In the list of files and folders, click **res** -- Click **values** (you will need to scroll down to find this folder; make sure you click the folder named **values** and not any of the folders named **values-xx** or with other suffixes) -- Click **strings.xml** -- Scroll down to Line #1657 (using the line numbers on the left-hand side of the text). This line should contain the text `` (note: if this line does not contain that text, use Ctrl+F, Cmd+F, or your browser’s Find on Page function to find the line which contains that text) -- Select and copy all text from that line down to the end of the file -- Open a plaintext editor (TextEdit on macOS, Notepad on Windows, or your preferred plaintext editor) and create a new file -- Paste the text you copied earlier into this blank file - -#### Step 2: Translate! -This file will now have a large number of lines of code. Each line will contain `` and `` tags. To translate Session Android, translate the text between these tags. For example: - -`Continue` - -In this line, translate the word **Continue** into the language you are translating into. Do not translate any other text. Do **not** translate the text inside either pair of angled brackets <>. - -Translate the word or sentence between each pair of `` tags, on each line. - -Once you are finished translating, save the file with a filename that specifies which language you have translated the app into, and email the file to us at support@getsession.org along with information about the language you have translated the app into. Be sure to specify in your email that you have translated the Android app (for information on translating the iOS app, see above). Our developers will then take your translation and apply it to the Session Android app. Thank you for helping make Session more accessible for everyone! diff --git a/app/build.gradle b/app/build.gradle index 48ed18bc2c..60bdb4e2bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,8 +143,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.2' } -def canonicalVersionCode = 169 -def canonicalVersionName = "1.10.7" +def canonicalVersionCode = 174 +def canonicalVersionName = "1.10.8" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 972ec30be1..3d223e42c0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -172,9 +172,6 @@ - - - - - - - - - - - - - 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) { poller.setCaughtUp(false); } startPollingIfNeeded(); - OpenGroupManager.INSTANCE.setAllCaughtUp(false); OpenGroupManager.INSTANCE.startPolling(); } @@ -217,13 +218,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc Log.i(TAG, "App is no longer visible."); KeyCachingService.onAppBackgrounded(this); messageNotifier.setVisibleThread(-1); - // Loki if (poller != null) { poller.stopIfNeeded(); } - if (closedGroupPoller != null) { - closedGroupPoller.stopIfNeeded(); - } + ClosedGroupPollerV2.getShared().stop(); } @Override @@ -314,13 +312,13 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc private void initializeJobManager() { this.jobManager = new JobManager(this, new JobManager.Configuration.Builder() - .setDataSerializer(new JsonDataSerializer()) - .setJobFactories(JobManagerFactories.getJobFactories(this)) - .setConstraintFactories(JobManagerFactories.getConstraintFactories(this)) - .setConstraintObservers(JobManagerFactories.getConstraintObservers(this)) - .setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this))) - .setDependencyInjector(this) - .build()); + .setDataSerializer(new JsonDataSerializer()) + .setJobFactories(JobManagerFactories.getJobFactories(this)) + .setConstraintFactories(JobManagerFactories.getConstraintFactories(this)) + .setConstraintObservers(JobManagerFactories.getConstraintObservers(this)) + .setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this))) + .setDependencyInjector(this) + .build()); } private void initializeDependencyInjection() { @@ -328,7 +326,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc this.objectGraph = ObjectGraph.create(communicationModule); } - private void initializeExpiringMessageManager() { this.expiringMessageManager = new ExpiringMessageManager(this); } @@ -350,7 +347,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc } private void initializePeriodicTasks() { - BackgroundPollWorker.schedulePeriodic(this); // Loki + BackgroundPollWorker.schedulePeriodic(this); if (BuildConfig.PLAY_STORE_DISABLED) { UpdateApkRefreshListener.schedule(this); @@ -404,20 +401,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base))); } - private static class ProviderInitializationException extends RuntimeException { - } - - // region Loki - public boolean setUpStorageAPIIfNeeded() { - String userPublicKey = TextSecurePreferences.getLocalNumber(this); - if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { - return false; - } - byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize(); - LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this); - FileServerAPI.Companion.configure(userPublicKey, userPrivateKey, apiDB); - return true; - } + private static class ProviderInitializationException extends RuntimeException { } public void registerForFCMIfNeeded(final Boolean force) { if (firebaseInstanceIdJob != null && firebaseInstanceIdJob.isActive() && !force) return; @@ -449,7 +433,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc return; } poller = new Poller(); - closedGroupPoller = new ClosedGroupPoller(); } public void startPollingIfNeeded() { @@ -457,9 +440,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc if (poller != null) { poller.startIfNeeded(); } - if (closedGroupPoller != null) { - closedGroupPoller.startIfNeeded(); - } + ClosedGroupPollerV2.getShared().start(); } private void resubmitProfilePictureIfNeeded() { @@ -496,19 +477,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc }); } - public void updateOpenGroupProfilePicturesIfNeeded() { - AsyncTask.execute(() -> { - byte[] profileKey = ProfileKeyUtil.getProfileKey(this); - String url = TextSecurePreferences.getProfilePictureURL(this); - Set servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers(); - for (String server : servers) { - if (profileKey != null) { - OpenGroupAPI.setProfilePicture(server, profileKey, url); - } - } - }); - } - public void clearAllData(boolean isMigratingToV2KeyPair) { String token = TextSecurePreferences.getFCMToken(this); if (token != null && !token.isEmpty()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java index f61186144c..314653743c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MessageDetailsActivity.java @@ -30,18 +30,15 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.loader.app.LoaderManager.LoaderCallbacks; import androidx.loader.content.Loader; - - import org.session.libsession.messaging.messages.visible.LinkPreview; import org.session.libsession.messaging.messages.visible.OpenGroupInvitation; import org.session.libsession.messaging.messages.visible.Quote; import org.session.libsession.messaging.messages.visible.VisibleMessage; -import org.session.libsession.messaging.open_groups.OpenGroup; +import org.session.libsession.messaging.open_groups.OpenGroupV2; import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.utilities.UpdateMessageData; import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus; @@ -264,7 +261,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity } toFrom.setText(toFromRes); long threadID = messageRecord.getThreadId(); - OpenGroup openGroup = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadID); + OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID); if (openGroup != null && messageRecord.isOutgoing()) { toFrom.setVisibility(View.GONE); separator.setVisibility(View.GONE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index 8608426055..d017e770f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -5,10 +5,9 @@ import android.text.TextUtils import com.google.protobuf.ByteString import org.greenrobot.eventbus.EventBus import org.session.libsession.database.MessageDataProvider -import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.sending_receiving.attachments.* import org.session.libsession.utilities.Address -import org.session.libsession.messaging.utilities.DotNetAPI +import org.session.libsession.utilities.UploadResult import org.session.libsession.utilities.Util import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.messages.SignalServiceAttachment @@ -104,11 +103,7 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) return smsDatabase.isOutgoingMessage(timestamp) || mmsDatabase.isOutgoingMessage(timestamp) } - override fun getOpenGroup(threadID: Long): OpenGroup? { - return null // TODO: Implement - } - - override fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: DotNetAPI.UploadResult) { + override fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) { val database = DatabaseFactory.getAttachmentDatabase(context) val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return val attachmentPointer = SignalServiceAttachmentPointer(uploadResult.id, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java index ce4f00c530..85cf9332bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/QuoteView.java @@ -13,24 +13,21 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; - import com.annimon.stream.Stream; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import org.session.libsession.messaging.open_groups.OpenGroup; +import org.session.libsession.messaging.contacts.Contact; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; - import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; - import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.RecipientModifiedListener; import org.session.libsession.utilities.TextSecurePreferences; @@ -197,17 +194,20 @@ public class QuoteView extends FrameLayout implements RecipientModifiedListener boolean outgoing = messageType != MESSAGE_TYPE_INCOMING; boolean isOwnNumber = Util.isOwnNumber(getContext(), author.getAddress().serialize()); - String quoteeDisplayName = author.toShortString(); + String quoteeDisplayName; - long threadID = DatabaseFactory.getThreadDatabase(getContext()).getOrCreateThreadIdFor(conversationRecipient); String senderHexEncodedPublicKey = author.getAddress().serialize(); - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadID); if (senderHexEncodedPublicKey.equalsIgnoreCase(TextSecurePreferences.getLocalNumber(getContext()))) { quoteeDisplayName = TextSecurePreferences.getProfileName(getContext()); - } else if (publicChat != null) { - quoteeDisplayName = DatabaseFactory.getLokiUserDatabase(getContext()).getServerDisplayName(publicChat.getId(), senderHexEncodedPublicKey); } 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); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 22504aa80b..c1f46a6bcf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -62,7 +62,6 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -77,9 +76,7 @@ import androidx.core.view.MenuItemCompat; import androidx.lifecycle.ViewModelProviders; import androidx.loader.app.LoaderManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import com.annimon.stream.Stream; - import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -90,7 +87,6 @@ import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessa import org.session.libsession.messaging.messages.signal.OutgoingTextMessage; import org.session.libsession.messaging.messages.visible.OpenGroupInvitation; import org.session.libsession.messaging.messages.visible.VisibleMessage; -import org.session.libsession.messaging.open_groups.OpenGroup; import org.session.libsession.messaging.open_groups.OpenGroupV2; import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; @@ -106,6 +102,7 @@ import org.session.libsession.utilities.recipients.RecipientModifiedListener; import org.session.libsession.utilities.ExpirationUtil; import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.MediaTypes; +import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.ServiceUtil; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; @@ -160,8 +157,6 @@ import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity; 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.utilities.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; @@ -193,7 +188,6 @@ import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.PushCharacterCalculator; - import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -204,7 +198,6 @@ import java.util.Locale; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; - import kotlin.Unit; import network.loki.messenger.R; @@ -217,14 +210,14 @@ import network.loki.messenger.R; */ @SuppressLint("StaticFieldLeak") public class ConversationActivity extends PassphraseRequiredActionBarActivity - implements ConversationFragment.ConversationFragmentListener, - AttachmentManager.AttachmentListener, - RecipientModifiedListener, - OnKeyboardShownListener, - InputPanel.Listener, - InputPanel.MediaListener, - ComposeText.CursorPositionChangedListener, - ConversationSearchBottomBar.EventListener + implements ConversationFragment.ConversationFragmentListener, + AttachmentManager.AttachmentListener, + RecipientModifiedListener, + OnKeyboardShownListener, + InputPanel.Listener, + InputPanel.MediaListener, + ComposeText.CursorPositionChangedListener, + ConversationSearchBottomBar.EventListener { private static final String TAG = ConversationActivity.class.getSimpleName(); @@ -239,11 +232,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public static final String LAST_SEEN_EXTRA = "last_seen"; 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_AUDIO = 3; 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 TAKE_PHOTO = 7; private static final int ADD_CONTACT = 8; @@ -377,12 +370,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(threadId, this); - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); OpenGroupV2 openGroupV2 = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId); - if (publicChat != null) { - // Request open group info update and handle the successful result in #onOpenGroupInfoUpdated(). - PublicChatInfoUpdateWorker.scheduleInstant(this, publicChat.getServer(), publicChat.getChannel()); - } else if (openGroupV2 != null) { + if (openGroupV2 != null) { PublicChatInfoUpdateWorker.scheduleInstant(this, openGroupV2.getServer(), openGroupV2.getRoom()); if (openGroupV2.getRoom().equals("session") || openGroupV2.getRoom().equals("oxen") || openGroupV2.getRoom().equals("lokinet") || openGroupV2.getRoom().equals("crypto")) { @@ -427,7 +416,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.i(TAG, "onNewIntent()"); - + if (isFinishing()) { Log.w(TAG, "Activity is finishing..."); return; @@ -522,90 +511,89 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity super.onActivityResult(reqCode, resultCode, data); if ((data == null && reqCode != TAKE_PHOTO && reqCode != SMS_DEFAULT) || - (resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) + (resultCode != RESULT_OK && reqCode != SMS_DEFAULT)) { updateLinkPreviewState(); return; } switch (reqCode) { - case PICK_DOCUMENT: - setMedia(data.getData(), MediaType.DOCUMENT); - break; - case PICK_AUDIO: - setMedia(data.getData(), MediaType.AUDIO); - break; - case TAKE_PHOTO: - if (attachmentManager.getCaptureUri() != null) { - setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); - } - break; - case ADD_CONTACT: - recipient = Recipient.from(this, recipient.getAddress(), true); - recipient.addListener(this); - fragment.reloadList(); - break; + case PICK_DOCUMENT: + setMedia(data.getData(), MediaType.DOCUMENT); + break; + case PICK_AUDIO: + setMedia(data.getData(), MediaType.AUDIO); + break; + case TAKE_PHOTO: + if (attachmentManager.getCaptureUri() != null) { + setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE); + } + break; + case ADD_CONTACT: + recipient = Recipient.from(this, recipient.getAddress(), true); + recipient.addListener(this); + fragment.reloadList(); + break; /* case PICK_LOCATION: SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); attachmentManager.setLocation(place, getCurrentMediaConstraints()); break; */ - case PICK_GIF: - setMedia(data.getData(), - MediaType.GIF, - data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), - data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); - break; - case SMS_DEFAULT: - initializeSecurity(true, isDefaultSms); - break; - case MEDIA_SENDER: - long expiresIn = recipient.getExpireMessages() * 1000L; - int subscriptionId = -1; - boolean initiating = threadId == -1; - String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); - SlideDeck slideDeck = new SlideDeck(); + case PICK_GIF: + setMedia(data.getData(), + MediaType.GIF, + data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0), + data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0)); + break; + case SMS_DEFAULT: + initializeSecurity(true, isDefaultSms); + break; + case MEDIA_SENDER: + long expiresIn = recipient.getExpireMessages() * 1000L; + int subscriptionId = -1; + boolean initiating = threadId == -1; + String message = data.getStringExtra(MediaSendActivity.EXTRA_MESSAGE); + SlideDeck slideDeck = new SlideDeck(); - List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); + List mediaList = data.getParcelableArrayListExtra(MediaSendActivity.EXTRA_MEDIA); - for (Media mediaItem : mediaList) { - if (MediaUtil.isVideoType(mediaItem.getMimeType())) { - slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); - } else if (MediaUtil.isGif(mediaItem.getMimeType())) { - slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); - } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { - slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); - } else { - Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping."); + for (Media mediaItem : mediaList) { + if (MediaUtil.isVideoType(mediaItem.getMimeType())) { + slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull())); + } else if (MediaUtil.isGif(mediaItem.getMimeType())) { + slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); + } else if (MediaUtil.isImageType(mediaItem.getMimeType())) { + slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull())); + } else { + 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, - slideDeck, - inputPanel.getQuote().orNull(), - Optional.absent(), - initiating).addListener(new AssertedSuccessListener() { - @Override - public void onSuccess(Void result) { - AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { - Stream.of(slideDeck.getSlides()) - .map(Slide::getUri) - .withoutNulls() - .filter(BlobProvider::isAuthority) - .forEach(uri -> BlobProvider.getInstance().delete(context, uri)); - }); - } - }); - - break; - case INVITE_CONTACTS: - if (data.getExtras() == null || !data.hasExtra(SelectContactsActivity.Companion.getSelectedContactsKey())) return; - String[] selectedContacts = data.getExtras().getStringArray(SelectContactsActivity.Companion.getSelectedContactsKey()); - sendOpenGroupInvitations(selectedContacts); - break; + sendMediaMessage(message, + slideDeck, + inputPanel.getQuote().orNull(), + Optional.absent(), + initiating).addListener(new AssertedSuccessListener() { + @Override + public void onSuccess(Void result) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { + Stream.of(slideDeck.getSlides()) + .map(Slide::getUri) + .withoutNulls() + .filter(BlobProvider::isAuthority) + .forEach(uri -> BlobProvider.getInstance().delete(context, uri)); + }); + } + }); + break; + case INVITE_CONTACTS: + if (data.getExtras() == null || !data.hasExtra(SelectContactsActivity.Companion.getSelectedContactsKey())) return; + String[] selectedContacts = data.getExtras().getStringArray(SelectContactsActivity.Companion.getSelectedContactsKey()); + sendOpenGroupInvitations(selectedContacts); + break; } } @@ -685,14 +673,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (isSingleConversation() && getRecipient().getContactUri() == null) { inflater.inflate(R.menu.conversation_add_to_contacts, menu); } - - if (recipient != null && recipient.isLocalNumber()) { if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false); else menu.findItem(R.id.menu_call_insecure).setVisible(false); - MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications); - if (muteItem != null) { muteItem.setVisible(false); } @@ -760,26 +744,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity switch (item.getItemId()) { // case R.id.menu_call_secure: handleDial(getRecipient(), true); return true; // case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; - case R.id.menu_unblock: handleUnblock(); return true; - case R.id.menu_block: handleBlock(); 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_add_shortcut: handleAddShortcut(); return true; - case R.id.menu_search: handleSearch(); return true; + case R.id.menu_unblock: handleUnblock(); return true; + case R.id.menu_block: handleBlock(); 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_add_shortcut: handleAddShortcut(); return true; + case R.id.menu_search: handleSearch(); 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_group_recipients: handleDisplayGroupRecipients(); 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_edit_group: handleEditPushGroup(); return true; - case R.id.menu_leave: handleLeavePushGroup(); return true; - case R.id.menu_mute_notifications: handleMuteNotifications(); return true; - case R.id.menu_unmute_notifications: handleUnmuteNotifications(); 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_edit_group: handleEditPushGroup(); return true; + case R.id.menu_leave: handleLeavePushGroup(); return true; + case R.id.menu_mute_notifications: handleMuteNotifications(); return true; + case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true; // case R.id.menu_conversation_settings: handleConversationSettings(); return true; - case R.id.menu_expiring_messages_off: - case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; - case R.id.menu_invite_to_open_group: handleInviteToOpenGroup(); return true; - case android.R.id.home: handleReturnToConversationList(); return true; + case R.id.menu_expiring_messages_off: + case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true; + case R.id.menu_invite_to_open_group: handleInviteToOpenGroup(); return true; + case android.R.id.home: handleReturnToConversationList(); return true; } return false; @@ -850,7 +834,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setMuted(recipient, until); + .setMuted(recipient, until); return null; } @@ -865,7 +849,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setMuted(recipient, 0); + .setMuted(recipient, 0); return null; } @@ -877,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; new AlertDialog.Builder(this) - .setTitle(titleRes) - .setMessage(bodyRes) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DatabaseFactory.getRecipientDatabase(ConversationActivity.this) - .setBlocked(recipient, false); + .setTitle(titleRes) + .setMessage(bodyRes) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.ConversationActivity_unblock, (dialog, which) -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientDatabase(ConversationActivity.this) + .setBlocked(recipient, false); - return null; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }).show(); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }).show(); } @TargetApi(Build.VERSION_CODES.KITKAT) @@ -960,7 +944,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (icon == null) { icon = IconCompat.createWithResource(context, recipient.isGroupRecipient() ? R.mipmap.ic_group_shortcut - : R.mipmap.ic_person_shortcut); + : R.mipmap.ic_person_shortcut); } return icon; @@ -970,14 +954,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity protected void onPostExecute(IconCompat icon) { Context context = getApplicationContext(); String name = Optional.fromNullable(recipient.getName()) - .or(Optional.fromNullable(recipient.getProfileName())) - .or(recipient.toShortString()); + .or(Optional.fromNullable(recipient.getProfileName())) + .or(recipient.toShortString()); ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, recipient.getAddress().serialize() + '-' + System.currentTimeMillis()) - .setShortLabel(name) - .setIcon(icon) - .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) - .build(); + .setShortLabel(name) + .setIcon(icon) + .setIntent(ShortcutLauncherActivity.createIntent(context, recipient.getAddress())) + .build(); if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) { Toast.makeText(context, getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show(); @@ -993,7 +977,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void handleLeavePushGroup() { if (getRecipient() == null) { Toast.makeText(this, getString(R.string.ConversationActivity_invalid_recipient), - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); return; } @@ -1061,7 +1045,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getThreadDatabase(ConversationActivity.this) - .setDistributionType(threadId, DistributionTypes.BROADCAST); + .setDistributionType(threadId, DistributionTypes.BROADCAST); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1077,7 +1061,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getThreadDatabase(ConversationActivity.this) - .setDistributionType(threadId, DistributionTypes.CONVERSATION); + .setDistributionType(threadId, DistributionTypes.CONVERSATION); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1109,7 +1093,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity /* Loki - We don't support SMS if (!isSecureText && !isPushGroupConversation()) sendButton.disableTransport(Type.TEXTSECURE); if (recipient.isPushGroupRecipient()) sendButton.disableTransport(Type.SMS); - if (!recipient.isPushGroupRecipient() && recipient.isForceSmsSelection()) { sendButton.setDefaultTransport(Type.SMS); } else { @@ -1419,13 +1402,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Subscribe(threadMode = ThreadMode.MAIN) public void onOpenGroupInfoUpdated(OpenGroupUtilities.GroupInfoUpdatedEvent event) { - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId); - if (publicChat != null && - publicChat.getChannel() == event.getChannel() && - publicChat.getServer().equals(event.getUrl())) { - this.updateSubtitleTextView(); - } if (openGroup != null && openGroup.getRoom().equals(event.getRoom()) && openGroup.getServer().equals(event.getUrl())) { @@ -1440,35 +1417,35 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity Log.i(TAG, "Selected: " + type); switch (type) { - case AttachmentTypeSelector.ADD_GALLERY: - AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break; - case AttachmentTypeSelector.ADD_DOCUMENT: - AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; - case AttachmentTypeSelector.ADD_SOUND: - AttachmentManager.selectAudio(this, PICK_AUDIO); break; - case AttachmentTypeSelector.ADD_CONTACT_INFO: - AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; - case AttachmentTypeSelector.ADD_LOCATION: - AttachmentManager.selectLocation(this, PICK_LOCATION); break; - case AttachmentTypeSelector.TAKE_PHOTO: - attachmentManager.capturePhoto(this, TAKE_PHOTO); break; - case AttachmentTypeSelector.ADD_GIF: - boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); - if (!hasSeenGIFMetaDataWarning) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Search GIFs?"); - builder.setMessage("You will not have full metadata protection when sending GIFs."); - builder.setPositiveButton("OK", (dialog, which) -> { + case AttachmentTypeSelector.ADD_GALLERY: + AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient, composeText.getTextTrimmed()); break; + case AttachmentTypeSelector.ADD_DOCUMENT: + AttachmentManager.selectDocument(this, PICK_DOCUMENT); break; + case AttachmentTypeSelector.ADD_SOUND: + AttachmentManager.selectAudio(this, PICK_AUDIO); break; + case AttachmentTypeSelector.ADD_CONTACT_INFO: + AttachmentManager.selectContactInfo(this, PICK_CONTACT); break; + case AttachmentTypeSelector.ADD_LOCATION: + AttachmentManager.selectLocation(this, PICK_LOCATION); break; + case AttachmentTypeSelector.TAKE_PHOTO: + attachmentManager.capturePhoto(this, TAKE_PHOTO); break; + case AttachmentTypeSelector.ADD_GIF: + boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this); + if (!hasSeenGIFMetaDataWarning) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Search GIFs?"); + builder.setMessage("You will not have full metadata protection when sending GIFs."); + builder.setPositiveButton("OK", (dialog, which) -> { + AttachmentManager.selectGif(this, PICK_GIF); + dialog.dismiss(); + }); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); + builder.create().show(); + TextSecurePreferences.setHasSeenGIFMetaDataWarning(this); + } else { 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); - } - break; + } + break; } } @@ -1563,8 +1540,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity draftDatabase.insertDrafts(threadId, drafts); threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), - drafts.getUriSnippet(), - System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); + drafts.getUriSnippet(), + System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); } else if (threadId > 0) { threadDatabase.update(threadId, false); } @@ -1667,10 +1644,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity String timestamp = new SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(new Date()); String filename = String.format("signal-%s.txt", timestamp); Uri textUri = BlobProvider.getInstance() - .forData(textData) - .withMimeType(MediaTypes.LONG_TEXT) - .withFileName(filename) - .createForSingleSessionInMemory(); + .forData(textData) + .withMimeType(MediaTypes.LONG_TEXT) + .withFileName(filename) + .createForSingleSessionInMemory(); textSlide = Optional.of(new TextSlide(this, textUri, filename, textData.length)); } @@ -1748,10 +1725,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize; boolean isMediaMessage = attachmentManager.isAttachmentPresent() || // recipient.isGroupRecipient() || - inputPanel.getQuote().isPresent() || - linkPreviewViewModel.hasLinkPreview() || - LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages - needsSplit; + inputPanel.getQuote().isPresent() || + linkPreviewViewModel.hasLinkPreview() || + LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages + needsSplit; if (isMediaMessage) { sendMediaMessage(initiating); @@ -1772,7 +1749,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void sendMediaMessage(boolean initiating) - throws InvalidMessageException + throws InvalidMessageException { Log.i(TAG, "Sending media message..."); sendMediaMessage(getMessage(), attachmentManager.buildSlideDeck(), inputPanel.getQuote().orNull(), linkPreviewViewModel.getActiveLinkPreview(), initiating); @@ -1832,7 +1809,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void sendTextMessage(final boolean initiating) - throws InvalidMessageException + throws InvalidMessageException { final Context context = getApplicationContext(); final String messageBody = getMessage(); @@ -1910,10 +1887,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void onRecorderPermissionRequired() { Permissions.with(this) - .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) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) - .execute(); + .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) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages)) + .execute(); } @Override @@ -2072,16 +2049,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void onClick(View v) { Permissions.with(ConversationActivity.this) - .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) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) - .onAllGranted(() -> { - composeText.clearFocus(); - startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER); - 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()) - .execute(); + .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) + .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video)) + .onAllGranted(() -> { + composeText.clearFocus(); + startActivityForResult(MediaSendActivity.buildCameraIntent(ConversationActivity.this, recipient), MEDIA_SENDER); + 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()) + .execute(); } } @@ -2191,7 +2168,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } if (text.length() > 0) { if (currentMentionStartIndex > text.length()) { - resetMentions(); // Should never occur + resetMentions(); // Should never occur } int lastCharacterIndex = text.length() - 1; char lastCharacter = text.charAt(lastCharacterIndex); @@ -2199,11 +2176,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (lastCharacterIndex > 0) { 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)) { - List mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId); + List mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates("", threadId, recipient.isOpenGroupRecipient()); currentMentionStartIndex = lastCharacterIndex; mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionView.show(mentionCandidates, threadId); @@ -2214,7 +2188,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } else { if (currentMentionStartIndex != -1) { String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @ - List mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId); + List mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates(query, threadId, recipient.isOpenGroupRecipient()); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionView.show(mentionCandidates, threadId); } @@ -2258,12 +2232,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - body, - slideDeck, - recipient, - threadId); + messageRecord.getDateSent(), + author, + body, + slideDeck, + recipient, + threadId); } else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) { LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0); @@ -2274,26 +2248,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - messageRecord.getBody(), - slideDeck, - recipient, - threadId); + messageRecord.getDateSent(), + author, + messageRecord.getBody(), + slideDeck, + recipient, + threadId); } else { inputPanel.setQuote(GlideApp.with(this), - messageRecord.getDateSent(), - author, - messageRecord.getBody(), - messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), - recipient, - threadId); + messageRecord.getDateSent(), + author, + messageRecord.getBody(), + messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(), + recipient, + threadId); } } @Override public void onMessageActionToolbarOpened() { - searchViewItem.collapseActionView(); + searchViewItem.collapseActionView(); } @Override @@ -2355,12 +2329,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void updateTitleTextView(Recipient recipient) { String userPublicKey = TextSecurePreferences.getLocalNumber(this); if (recipient == null) { - titleTextView.setText("Compose"); + titleTextView.setText(R.string.ConversationActivity_compose); } else if (recipient.getAddress().toString().toLowerCase().equals(userPublicKey)) { - titleTextView.setText(getResources().getString(R.string.note_to_self)); + titleTextView.setText(R.string.note_to_self); } else { - boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); - titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString()); + String displayName = recipient.getName(); // Uses the Contact API internally + boolean hasName = (displayName != null); + titleTextView.setText(hasName ? displayName : recipient.getAddress().toString()); } } @@ -2378,18 +2353,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity subtitleTextView.setVisibility(View.VISIBLE); if (recipient.isMuted()) { muteIndicatorImageView.setVisibility(View.VISIBLE); - subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())); + subtitleTextView.setText(getString(R.string.ConversationActivity_muted_until_date,DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()))); } else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) { - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId); OpenGroupV2 openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadId); - if (publicChat != null) { - Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer()); - if (userCount == null) { userCount = 0; } - subtitleTextView.setText(userCount + " members"); - } else if (openGroup != null) { + if (openGroup != null) { Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(openGroup.getRoom(),openGroup.getServer()); if (userCount == null) { userCount = 0; } - subtitleTextView.setText(userCount + " members"); + subtitleTextView.setText(getString(R.string.ConversationActivity_member_count,userCount)); } else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) { subtitleTextView.setText(recipient.getAddress().toString()); } else { @@ -2411,7 +2381,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void updateMessageStatusProgressBar() { if (messageStatus != null) { - messageStatusProgressBar.setAlpha(1.0f); + messageStatusProgressBar.setAlpha(1.0f); switch (messageStatus) { case "calculatingPoW": setMessageStatusProgressAnimatedIfPossible(25); break; case "contactingNetwork": setMessageStatusProgressAnimatedIfPossible(50); break; @@ -2448,7 +2418,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity default: return -1; } } else { - return -1; + return -1; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 275cbba606..425160b45d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -54,17 +54,13 @@ import androidx.loader.content.Loader; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnScrollListener; - import com.annimon.stream.Stream; - import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.messaging.messages.control.DataExtractionNotification; import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage; import org.session.libsession.messaging.messages.signal.OutgoingTextMessage; import org.session.libsession.messaging.messages.visible.Quote; import org.session.libsession.messaging.messages.visible.VisibleMessage; -import org.session.libsession.messaging.open_groups.OpenGroup; -import org.session.libsession.messaging.open_groups.OpenGroupAPI; import org.session.libsession.messaging.open_groups.OpenGroupAPIV2; import org.session.libsession.messaging.open_groups.OpenGroupV2; import org.session.libsession.messaging.sending_receiving.MessageSender; @@ -398,9 +394,8 @@ public class ConversationFragment extends Fragment boolean isGroupChat = recipient.isGroupRecipient(); if (isGroupChat) { - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId); - boolean isPublicChat = (publicChat != null || openGroupChat != null); + boolean isPublicChat = (openGroupChat != null); int selectedMessageCount = messageRecords.size(); boolean areAllSentByUser = true; Set uniqueUserSet = new HashSet<>(); @@ -412,10 +407,7 @@ public class ConversationFragment extends Fragment menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()); boolean userCanModerate = - (isPublicChat && - ((publicChat != null && OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer())) - || (openGroupChat != null && OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer()))) - ); + (isPublicChat && (OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer()))); boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate); // allow banning if moderating a public chat and only one user's messages are selected boolean isBanOptionVisible = isPublicChat && userCanModerate && !areAllSentByUser && uniqueUserSet.size() == 1; @@ -515,7 +507,6 @@ public class ConversationFragment extends Fragment builder.setMessage(getActivity().getResources().getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messagesCount, messagesCount)); builder.setCancelable(true); - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId); builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { @@ -527,7 +518,7 @@ public class ConversationFragment extends Fragment { @Override protected Void doInBackground(MessageRecord... messageRecords) { - if (publicChat != null || openGroupChat != null) { + if (openGroupChat != null) { ArrayList serverIDs = new ArrayList<>(); ArrayList ignoredMessages = new ArrayList<>(); ArrayList failedMessages = new ArrayList<>(); @@ -541,29 +532,7 @@ public class ConversationFragment extends Fragment ignoredMessages.add(messageRecord.getId()); } } - if (publicChat != null) { - OpenGroupAPI - .deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser) - .success(l -> { - for (MessageRecord messageRecord : messageRecords) { - Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms()); - if (l.contains(serverID)) { - if (messageRecord.isMms()) { - DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); - } else { - DatabaseFactory.getSmsDatabase(getActivity()).deleteMessage(messageRecord.getId()); - } - } else if (!ignoredMessages.contains(serverID)) { - failedMessages.add(messageRecord.getId()); - Log.w("Loki", "Failed to delete message: " + messageRecord.getId() + "."); - } - } - return null; - }). fail(e -> { - Log.w("Loki", "Couldn't delete message due to error: " + e.toString() + "."); - return null; - }); - } else if (openGroupChat != null) { + if (openGroupChat != null) { for (Long serverId : serverIDs) { OpenGroupAPIV2 .deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer()) @@ -617,7 +586,6 @@ public class ConversationFragment extends Fragment builder.setTitle(R.string.ConversationFragment_ban_selected_user); builder.setCancelable(true); - final OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); final OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId); builder.setPositiveButton(R.string.ban, (dialog, which) -> { @@ -630,17 +598,7 @@ public class ConversationFragment extends Fragment @Override protected Void doInBackground(String... userPublicKeyParam) { String userPublicKey = userPublicKeyParam[0]; - if (publicChat != null) { - OpenGroupAPI - .ban(userPublicKey, publicChat.getServer()) - .success(l -> { - Log.d("Loki", "User banned"); - return Unit.INSTANCE; - }).fail(e -> { - Log.e("Loki", "Couldn't ban user due to error",e); - return null; - }); - } else if (openGroupChat != null) { + if (openGroupChat != null) { OpenGroupAPIV2 .ban(userPublicKey, openGroupChat.getRoom(), openGroupChat.getServer()) .success(l -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 1454e94336..c089feed6b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -45,17 +45,14 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.DimenRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - 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.JobQueue; -import org.session.libsession.messaging.open_groups.OpenGroup; -import org.session.libsession.messaging.open_groups.OpenGroupAPI; import org.session.libsession.messaging.open_groups.OpenGroupAPIV2; import org.session.libsession.messaging.open_groups.OpenGroupV2; import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress; @@ -760,10 +757,6 @@ public class ConversationItem extends LinearLayout String publicKey = recipient.getAddress().toString(); profilePictureView.setPublicKey(publicKey); String displayName = recipient.getName(); - OpenGroup openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID); - if (displayName == null && openGroup != null) { - displayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(openGroup.getId(), publicKey); - } profilePictureView.setDisplayName(displayName); profilePictureView.setAdditionalPublicKey(null); profilePictureView.setRSSFeed(false); @@ -898,19 +891,14 @@ public class ConversationItem extends LinearLayout @SuppressLint("SetTextI18n") private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) { if (groupThread && !messageRecord.isOutgoing()) { - // Show custom display names for group chats - String displayName = recipient.toShortString(); - try { - String serverId = GroupUtil.getDecodedGroupID(conversationRecipient.getAddress().serialize()); - String senderDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverId, recipient.getAddress().serialize()); - if (senderDisplayName != null) { - displayName = senderDisplayName; - } else { - // opengroupv2 format - displayName = OpenGroupUtilities.getDisplayName(recipient); - } - } catch (Exception e) { - // Do nothing + 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); @@ -952,12 +940,8 @@ public class ConversationItem extends LinearLayout profilePictureView.setVisibility(VISIBLE); int visibility = View.GONE; - OpenGroup publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(messageRecord.getThreadId()); OpenGroupV2 openGroupV2 = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(messageRecord.getThreadId()); - if (publicChat != null) { - boolean isModerator = OpenGroupAPI.isUserModerator(current.getRecipient().getAddress().toString(), publicChat.getChannel(), publicChat.getServer()); - visibility = isModerator ? View.VISIBLE : View.GONE; - } else if (openGroupV2 != null) { + if (openGroupV2 != null) { boolean isModerator = OpenGroupAPIV2.isUserModerator(current.getRecipient().getAddress().toString(), openGroupV2.getRoom(), openGroupV2.getServer()); visibility = isModerator ? View.VISIBLE : View.GONE; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Database.java b/app/src/main/java/org/thoughtcrime/securesms/database/Database.java index 0736a56933..b1d1810a5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Database.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Database.java @@ -41,7 +41,7 @@ public abstract class Database { public Database(Context context, SQLCipherOpenHelper databaseHelper) { this.context = context; this.databaseHelper = databaseHelper; - this.threadNotificationDebouncer = new Debouncer(ApplicationContext.getInstance(context).getThreadNotificationHandler(), 100); + this.threadNotificationDebouncer = new Debouncer(ApplicationContext.getInstance(context).getThreadNotificationHandler(), 250); } protected void notifyConversationListeners(Set threadIds) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java index ea534c35f8..9a87fa8730 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -17,11 +17,8 @@ package org.thoughtcrime.securesms.database; import android.content.Context; - import androidx.annotation.NonNull; - import net.sqlcipher.database.SQLiteDatabase; - import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider; import org.thoughtcrime.securesms.crypto.AttachmentSecret; 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.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.SessionJobDatabase; +import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; public class DatabaseFactory { @@ -55,16 +53,13 @@ public class DatabaseFactory { private final GroupReceiptDatabase groupReceiptDatabase; private final SearchDatabase searchDatabase; private final JobDatabase jobDatabase; - - // Loki private final LokiAPIDatabase lokiAPIDatabase; private final LokiMessageDatabase lokiMessageDatabase; private final LokiThreadDatabase lokiThreadDatabase; private final LokiUserDatabase lokiUserDatabase; private final LokiBackupFilesDatabase lokiBackupFilesDatabase; private final SessionJobDatabase sessionJobDatabase; - - // Refactor + private final SessionContactDatabase sessionContactDatabase; private final Storage storage; private final DatabaseAttachmentProvider attachmentProvider; @@ -157,6 +152,10 @@ public class DatabaseFactory { public static SessionJobDatabase getSessionJobDatabase(Context context) { return getInstance(context).sessionJobDatabase; } + + public static SessionContactDatabase getSessionContactDatabase(Context context) { + return getInstance(context).sessionContactDatabase; + } // endregion // region Refactor @@ -202,6 +201,7 @@ public class DatabaseFactory { this.storage = new Storage(context, databaseHelper); this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper); this.sessionJobDatabase = new SessionJobDatabase(context, databaseHelper); + this.sessionContactDatabase = new SessionContactDatabase(context, databaseHelper); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index d2b6ace217..287a6d365c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -248,6 +248,7 @@ public class RecipientDatabase extends Database { ContentValues contentValues = new ContentValues(1); contentValues.put(SYSTEM_DISPLAY_NAME, profileName); updateOrInsert(recipient.getAddress(), contentValues); + recipient.resolve().setName(profileName); recipient.resolve().setProfileName(profileName); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index d1e620a117..5f498fa100 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -2,18 +2,14 @@ package org.thoughtcrime.securesms.database import android.content.Context import android.net.Uri -import okhttp3.HttpUrl import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.jobs.AttachmentUploadJob -import org.session.libsession.messaging.jobs.Job -import org.session.libsession.messaging.jobs.JobQueue -import org.session.libsession.messaging.jobs.MessageSendJob +import org.session.libsession.messaging.jobs.* +import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.messages.control.ConfigurationMessage import org.session.libsession.messaging.messages.signal.* import org.session.libsession.messaging.messages.signal.IncomingTextMessage import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage -import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroupV2 import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment @@ -40,12 +36,12 @@ import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob import org.thoughtcrime.securesms.loki.api.OpenGroupManager import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol -import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities import org.thoughtcrime.securesms.loki.utilities.get import org.thoughtcrime.securesms.loki.utilities.getString import org.thoughtcrime.securesms.mms.PartAuthority class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { + override fun getUserPublicKey(): String? { return TextSecurePreferences.getLocalNumber(context) } @@ -72,30 +68,13 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return TextSecurePreferences.getProfilePictureURL(context) } - override fun setUserProfilePictureUrl(newProfilePicture: String) { + override fun setUserProfilePictureURL(newValue: String) { val ourRecipient = Address.fromSerialized(getUserPublicKey()!!).let { Recipient.from(context, it, false) } - TextSecurePreferences.setProfilePictureURL(context, newProfilePicture) - RetrieveProfileAvatarJob(ourRecipient, newProfilePicture) - ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, newProfilePicture)) - } - - 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) + TextSecurePreferences.setProfilePictureURL(context, newValue) + RetrieveProfileAvatarJob(ourRecipient, newValue) + ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, newValue)) } override fun getOrGenerateRegistrationID(): Int { @@ -107,15 +86,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return registrationID } - override fun persistAttachments(messageId: Long, attachments: List): List { + override fun persistAttachments(messageID: Long, attachments: List): List { val database = DatabaseFactory.getAttachmentDatabase(context) val databaseAttachments = attachments.mapNotNull { it.toSignalAttachment() } - return database.insertAttachments(messageId, databaseAttachments) + return database.insertAttachments(messageID, databaseAttachments) } - override fun getAttachmentsForMessage(messageId: Long): List { + override fun getAttachmentsForMessage(messageID: Long): List { val database = DatabaseFactory.getAttachmentDatabase(context) - return database.getAttachmentsForMessage(messageId) + return database.getAttachmentsForMessage(messageID) } override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?, attachments: List): Long? { @@ -185,7 +164,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return messageID } - // JOBS override fun persistJob(job: Job) { DatabaseFactory.getSessionJobDatabase(context).persistJob(job) } @@ -210,6 +188,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return DatabaseFactory.getSessionJobDatabase(context).getMessageSendJob(messageSendJobID) } + override fun getMessageReceiveJob(messageReceiveJobID: String): MessageReceiveJob? { + return DatabaseFactory.getSessionJobDatabase(context).getMessageReceiveJob(messageReceiveJobID) + } + override fun resumeMessageSendJobIfNeeded(messageSendJobID: String) { val job = DatabaseFactory.getSessionJobDatabase(context).getMessageSendJob(messageSendJobID) ?: return JobQueue.shared.add(job) @@ -219,20 +201,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return DatabaseFactory.getSessionJobDatabase(context).isJobCanceled(job) } - // Authorization - - override fun getAuthToken(server: String): String? { - return DatabaseFactory.getLokiAPIDatabase(context).getAuthToken(server) - } - - override fun setAuthToken(server: String, newValue: String?) { - DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(server, newValue) - } - - override fun removeAuthToken(server: String) { - DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(server, null) - } - override fun getAuthToken(room: String, server: String): String? { val id = "$server.$room" return DatabaseFactory.getLokiAPIDatabase(context).getAuthToken(id) @@ -248,30 +216,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(id, null) } - override fun getOpenGroup(threadID: String): OpenGroup? { - if (threadID.toInt() < 0) { return null } - val database = databaseHelper.readableDatabase - return database.get(LokiThreadDatabase.publicChatTable, "${LokiThreadDatabase.threadID} = ?", arrayOf(threadID)) { cursor -> - val publicChatAsJSON = cursor.getString(LokiThreadDatabase.publicChat) - OpenGroup.fromJSON(publicChatAsJSON) - } - } - - override fun getV2OpenGroup(threadId: String): OpenGroupV2? { + override fun getV2OpenGroup(threadId: Long): OpenGroupV2? { if (threadId.toInt() < 0) { return null } val database = databaseHelper.readableDatabase - return database.get(LokiThreadDatabase.publicChatTable, "${LokiThreadDatabase.threadID} = ?", arrayOf(threadId)) { cursor -> + return database.get(LokiThreadDatabase.publicChatTable, "${LokiThreadDatabase.threadID} = ?", arrayOf( threadId.toString() )) { cursor -> val publicChatAsJson = cursor.getString(LokiThreadDatabase.publicChat) OpenGroupV2.fromJSON(publicChatAsJson) } } - override fun getThreadID(openGroupID: String): String { - val address = Address.fromSerialized(openGroupID) - val recipient = Recipient.from(context, address, false) - return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient).toString() - } - override fun getOpenGroupPublicKey(server: String): String? { return DatabaseFactory.getLokiAPIDatabase(context).getOpenGroupPublicKey(server) } @@ -280,59 +233,27 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getLokiAPIDatabase(context).setOpenGroupPublicKey(server, newValue) } - override fun setOpenGroupDisplayName(publicKey: String, channel: Long, server: String, displayName: String) { - val groupID = "$server.$channel" - DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(groupID, publicKey, displayName) - } - - override fun setOpenGroupDisplayName(publicKey: String, room: String, server: String, displayName: String) { - val groupID = "$server.$room" - DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(groupID, publicKey, displayName) - } - - override fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String? { - val groupID = "$server.$channel" - return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey) - } - - override fun getOpenGroupDisplayName(publicKey: String, room: String, server: String): String? { - val groupID = "$server.$room" - return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey) - } - - override fun getLastMessageServerId(room: String, server: String): Long? { + override fun getLastMessageServerID(room: String, server: String): Long? { return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(room, server) } - override fun setLastMessageServerId(room: String, server: String, newValue: Long) { + override fun setLastMessageServerID(room: String, server: String, newValue: Long) { DatabaseFactory.getLokiAPIDatabase(context).setLastMessageServerID(room, server, newValue) } - override fun removeLastMessageServerId(room: String, server: String) { + override fun removeLastMessageServerID(room: String, server: String) { DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(room, server) } - override fun getLastMessageServerID(group: Long, server: String): Long? { - return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(group, server) - } - - override fun setLastMessageServerID(group: Long, server: String, newValue: Long) { - DatabaseFactory.getLokiAPIDatabase(context).setLastMessageServerID(group, server, newValue) - } - - override fun removeLastMessageServerID(group: Long, server: String) { - DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(group, server) - } - - override fun getLastDeletionServerId(room: String, server: String): Long? { + override fun getLastDeletionServerID(room: String, server: String): Long? { return DatabaseFactory.getLokiAPIDatabase(context).getLastDeletionServerID(room, server) } - override fun setLastDeletionServerId(room: String, server: String, newValue: Long) { + override fun setLastDeletionServerID(room: String, server: String, newValue: Long) { DatabaseFactory.getLokiAPIDatabase(context).setLastDeletionServerID(room, server, newValue) } - override fun removeLastDeletionServerId(room: String, server: String) { + override fun removeLastDeletionServerID(room: String, server: String) { DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(room, server) } @@ -340,34 +261,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getLokiAPIDatabase(context).setUserCount(room, server, newValue) } - override fun getLastDeletionServerID(group: Long, server: String): Long? { - return DatabaseFactory.getLokiAPIDatabase(context).getLastDeletionServerID(group, server) - } - - override fun setLastDeletionServerID(group: Long, server: String, newValue: Long) { - DatabaseFactory.getLokiAPIDatabase(context).setLastDeletionServerID(group, server, newValue) - } - - override fun removeLastDeletionServerID(group: Long, server: String) { - DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(group, server) + override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean) { + DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID, isSms) + DatabaseFactory.getLokiMessageDatabase(context).setOriginalThreadID(messageID, serverID, threadID) } override fun isDuplicateMessage(timestamp: Long): Boolean { return getReceivedMessageTimestamps().contains(timestamp) } - override fun setUserCount(group: Long, server: String, newValue: Int) { - DatabaseFactory.getLokiAPIDatabase(context).setUserCount(group, server, newValue) - } - - override fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String) { - DatabaseFactory.getLokiAPIDatabase(context).setOpenGroupProfilePictureURL(group, server, newValue) - } - - override fun getOpenGroupProfilePictureURL(group: Long, server: String): String? { - return DatabaseFactory.getLokiAPIDatabase(context).getOpenGroupProfilePictureURL(group, server) - } - override fun updateTitle(groupID: String, newValue: String) { DatabaseFactory.getGroupDatabase(context).updateTitle(groupID, newValue) } @@ -394,15 +296,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return database.getMessageFor(timestamp, address)?.getId() } - override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean) { - DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID, isSms) - DatabaseFactory.getLokiMessageDatabase(context).setOriginalThreadID(messageID, serverID, threadID) - } - - override fun getQuoteServerID(quoteID: Long, publicKey: String): Long? { - return DatabaseFactory.getLokiMessageDatabase(context).getQuoteServerID(quoteID, publicKey) - } - override fun markAsSent(timestamp: Long, author: String) { val database = DatabaseFactory.getMmsSmsDatabase(context) val messageRecord = database.getMessageFor(timestamp, author) ?: return @@ -461,7 +354,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getGroupDatabase(context).setActive(groupID, value) } - override fun getZombieMember(groupID: String): Set { + override fun getZombieMembers(groupID: String): Set { return DatabaseFactory.getGroupDatabase(context).getGroupZombieMembers(groupID).map { it.address.serialize() }.toHashSet() } @@ -473,7 +366,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members) } - override fun updateZombieMembers(groupID: String, members: List
) { + override fun setZombieMembers(groupID: String, members: List
) { DatabaseFactory.getGroupDatabase(context).updateZombieMembers(groupID, members) } @@ -539,39 +432,18 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getLokiAPIDatabase(context).removeAllClosedGroupEncryptionKeyPairs(groupPublicKey) } - override fun getAllOpenGroups(): Map { - return DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().mapValues { (_,chat)-> - OpenGroup(chat.channel, chat.server, chat.displayName, chat.isDeletable) - } - } - override fun getAllV2OpenGroups(): Map { return DatabaseFactory.getLokiThreadDatabase(context).getAllV2OpenGroups() } - override fun addOpenGroup(serverUrl: String, channel: Long) { - val httpUrl = HttpUrl.parse(serverUrl) ?: return - if (httpUrl.queryParameterNames().contains("public_key")) { - // open group v2 - val server = HttpUrl.Builder().scheme(httpUrl.scheme()).host(httpUrl.host()).apply { - if (httpUrl.port() != 80 || httpUrl.port() != 443) { - // non-standard port, add to server - this.port(httpUrl.port()) - } - }.build() - val room = httpUrl.pathSegments().firstOrNull() ?: return - val publicKey = httpUrl.queryParameter("public_key") ?: return - - OpenGroupManager.add(server.toString().removeSuffix("/"), room, publicKey, context) - } else { - // TODO: No longer supported so let's remove this code - } - } - override fun getAllGroups(): List { return DatabaseFactory.getGroupDatabase(context).allGroups } + override fun addOpenGroup(urlAsString: String) { + OpenGroupManager.addOpenGroup(urlAsString, context) + } + override fun setProfileSharing(address: Address, value: Boolean) { val recipient = Recipient.from(context, address, false) DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, value) @@ -596,8 +468,17 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } } - override fun getThreadIdFor(address: Address): Long? { + override fun getThreadId(publicKeyOrOpenGroupID: String): Long? { + val address = Address.fromSerialized(publicKeyOrOpenGroupID) + return getThreadId(address) + } + + override fun getThreadId(address: Address): Long? { val recipient = Recipient.from(context, address, false) + return getThreadId(recipient) + } + + override fun getThreadId(recipient: Recipient): Long? { val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient) return if (threadID < 0) null else threadID } @@ -611,36 +492,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return threadId } - override fun getSessionRequestSentTimestamp(publicKey: String): Long? { - return DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestSentTimestamp(publicKey) + override fun getContactWithSessionID(sessionID: String): Contact? { + return DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(sessionID) } - override fun setSessionRequestSentTimestamp(publicKey: String, newValue: Long) { - DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestSentTimestamp(publicKey, newValue) + override fun getAllContacts(): Set { + return DatabaseFactory.getSessionContactDatabase(context).getAllContacts() } - override fun getSessionRequestProcessedTimestamp(publicKey: String): Long? { - return DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestProcessedTimestamp(publicKey) - } - - override fun setSessionRequestProcessedTimestamp(publicKey: String, newValue: Long) { - DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, newValue) - } - - override fun getDisplayName(publicKey: String): String? { - return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) - } - - override fun setDisplayName(publicKey: String, newName: String) { - DatabaseFactory.getLokiUserDatabase(context).setDisplayName(publicKey, newName) - } - - override fun getServerDisplayName(serverID: String, publicKey: String): String? { - return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverID, publicKey) - } - - override fun getProfilePictureURL(publicKey: String): String? { - return DatabaseFactory.getLokiUserDatabase(context).getProfilePictureURL(publicKey) + override fun setContact(contact: Contact) { + DatabaseFactory.getSessionContactDatabase(context).setContact(contact) } override fun getRecipientSettings(address: Address): Recipient.RecipientSettings? { @@ -669,10 +530,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, threadDatabase.getOrCreateThreadIdFor(recipient) } if (contacts.isNotEmpty()) { - threadDatabase.notifyUpdatedFromConfig() + threadDatabase.notifyConversationListListeners() } } + override fun getLastUpdated(threadID: Long): Long { + val threadDB = DatabaseFactory.getThreadDatabase(context) + return threadDB.getLastUpdated(threadID) + } + override fun getAttachmentDataUri(attachmentId: AttachmentId): Uri { return PartAuthority.getAttachmentDataUri(attachmentId) } @@ -681,7 +547,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return PartAuthority.getAttachmentThumbnailUri(attachmentId) } - // Data Extraction Notification override fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, sentTimestamp: Long) { val database = DatabaseFactory.getMmsDatabase(context) val address = fromSerialized(senderPublicKey) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index 98a6cf667e..ebb0dbab26 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -64,7 +64,7 @@ public class ThreadDatabase extends Database { private static final String TAG = ThreadDatabase.class.getSimpleName(); - private Map addressCache = new HashMap<>(); + private final Map addressCache = new HashMap<>(); public static final String TABLE_NAME = "thread"; public static final String ID = "_id"; @@ -404,6 +404,21 @@ public class ThreadDatabase extends Database { } } + public Long getLastUpdated(long threadId) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + Cursor cursor = db.query(TABLE_NAME, new String[]{DATE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + + try { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(0); + } + + return -1L; + } finally { + if (cursor != null) cursor.close(); + } + } + public void deleteConversation(long threadId) { DatabaseFactory.getSmsDatabase(context).deleteThread(threadId); DatabaseFactory.getMmsDatabase(context).deleteThread(threadId); @@ -471,7 +486,6 @@ public class ThreadDatabase extends Database { } public @Nullable Recipient getRecipientForThreadId(long threadId) { - // Loki - Cache the address if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) { return Recipient.from(context, addressCache.get(threadId), false); } @@ -505,10 +519,6 @@ public class ThreadDatabase extends Database { notifyConversationListeners(threadId); } - public void notifyUpdatedFromConfig() { - notifyConversationListListeners(); - } - public boolean update(long threadId, boolean unarchive) { MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); long count = mmsSmsDatabase.getConversationCount(threadId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 034fd7f521..349f7a082b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; 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.protocol.ClosedGroupsMigration; @@ -56,9 +57,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV22 = 43; private static final int lokiV23 = 44; private static final int lokiV24 = 45; + 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 - private static final int DATABASE_VERSION = lokiV24; + private static final int DATABASE_VERSION = lokiV26; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -123,11 +126,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand()); db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand()); - db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType()); db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable()); + db.execSQL(SessionContactDatabase.getCreateSessionContactTableCommand()); executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS); @@ -291,6 +294,16 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiAPIDatabase.getCreateSwarmTableCommand()); } + if (oldVersion < lokiV25) { + String jobTable = SessionJobDatabase.sessionJobTable; + db.execSQL("DROP TABLE " + jobTable); + db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); + } + + if (oldVersion < lokiV26) { + db.execSQL(SessionContactDatabase.getCreateSessionContactTableCommand()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java index 1c7bf1142f..3cabdce5d7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java @@ -92,7 +92,7 @@ public class AvatarDownloadJob extends BaseJob implements InjectableType { SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), url); if (pointer.getUrl().isEmpty()) throw new InvalidMessageException("Missing attachment URL."); - DownloadUtilities.downloadFile(attachment, pointer.getUrl(), MAX_AVATAR_SIZE, null); + DownloadUtilities.downloadFile(attachment, pointer.getUrl()); // Assume we're retrieving an attachment for an open group server if the digest is not set InputStream inputStream; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java index 080ce6c88c..34a010000e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java @@ -100,7 +100,7 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType File downloadDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir()); try { - DownloadUtilities.downloadFile(downloadDestination, profileAvatar, MAX_PROFILE_SIZE_BYTES, null); + DownloadUtilities.downloadFile(downloadDestination, profileAvatar); InputStream avatarStream = new ProfileCipherInputStream(new FileInputStream(downloadDestination), profileKey); File decryptDestination = File.createTempFile("avatar", ".jpg", context.getCacheDir()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt index 6450115184..a1bc90237b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt @@ -28,7 +28,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R -import network.loki.messenger.databinding.ActivityBackupRestoreBinding import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.backup.FullBackupImporter @@ -61,31 +60,31 @@ class BackupRestoreActivity : BaseActionBarActivity() { super.onCreate(savedInstanceState) setUpActionBarSessionLogo() - val viewBinding = DataBindingUtil.setContentView(this, R.layout.activity_backup_restore) - viewBinding.lifecycleOwner = this - viewBinding.viewModel = viewModel +// val viewBinding = DataBindingUtil.setContentView(this, R.layout.activity_backup_restore) +// viewBinding.lifecycleOwner = this +// viewBinding.viewModel = viewModel - viewBinding.restoreButton.setOnClickListener { viewModel.tryRestoreBackup() } +// viewBinding.restoreButton.setOnClickListener { viewModel.tryRestoreBackup() } - viewBinding.buttonSelectFile.setOnClickListener { - fileSelectionResultLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply { - //FIXME On some old APIs (tested on 21 & 23) the mime type doesn't filter properly - // and the backup files are unavailable for selection. -// type = BackupUtil.BACKUP_FILE_MIME_TYPE - type = "*/*" - }) - } +// viewBinding.buttonSelectFile.setOnClickListener { +// fileSelectionResultLauncher.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply { +// //FIXME On some old APIs (tested on 21 & 23) the mime type doesn't filter properly +// // and the backup files are unavailable for selection. +//// type = BackupUtil.BACKUP_FILE_MIME_TYPE +// type = "*/*" +// }) +// } - viewBinding.backupCode.addTextChangedListener { text -> viewModel.backupPassphrase.value = text.toString() } +// viewBinding.backupCode.addTextChangedListener { text -> viewModel.backupPassphrase.value = text.toString() } // Focus passphrase text edit when backup file is selected. - viewModel.backupFile.observe(this, { backupFile -> - if (backupFile != null) viewBinding.backupCode.post { - viewBinding.backupCode.requestFocus() - (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) - .showSoftInput(viewBinding.backupCode, InputMethodManager.SHOW_IMPLICIT) - } - }) +// viewModel.backupFile.observe(this, { backupFile -> +// if (backupFile != null) viewBinding.backupCode.post { +// viewBinding.backupCode.requestFocus() +// (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) +// .showSoftInput(viewBinding.backupCode, InputMethodManager.SHOW_IMPLICIT) +// } +// }) // React to backup import result. viewModel.backupImportResult.observe(this) { result -> @@ -116,8 +115,8 @@ class BackupRestoreActivity : BaseActionBarActivity() { openURL("https://getsession.org/privacy-policy/") } }, 61, 75, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - viewBinding.termsTextView.movementMethod = LinkMovementMethod.getInstance() - viewBinding.termsTextView.text = termsExplanation +// viewBinding.termsTextView.movementMethod = LinkMovementMethod.getInstance() +// viewBinding.termsTextView.text = termsExplanation //endregion } @@ -190,7 +189,6 @@ class BackupRestoreViewModel(application: Application): AndroidViewModel(applica TextSecurePreferences.setHasViewedSeed(context, true) TextSecurePreferences.setHasSeenWelcomeScreen(context, true) val application = ApplicationContext.getInstance(context) - application.setUpStorageAPIIfNeeded() BackupRestoreResult.SUCCESS } catch (e: DatabaseDowngradeException) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 1b54c5010f..0e5ccd6fde 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -30,8 +30,8 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.mentions.MentionsManager -import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.messaging.sending_receiving.MessageSender +import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV2 import org.session.libsession.utilities.* import org.session.libsignal.utilities.toHexString import org.session.libsignal.utilities.ThreadUtils @@ -133,12 +133,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), }) // Set up remaining components if needed 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) if (userPublicKey != null) { - MentionsManager.configureIfNeeded(userPublicKey, userDB) OpenGroupManager.startPolling() JobQueue.shared.resumePendingJobs() } @@ -332,16 +328,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } } // Delete the conversation - val v1OpenGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) val v2OpenGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID) - if (v1OpenGroup != null) { - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - apiDB.removeLastMessageServerID(v1OpenGroup.channel, v1OpenGroup.server) - apiDB.removeLastDeletionServerID(v1OpenGroup.channel, v1OpenGroup.server) - apiDB.clearOpenGroupProfilePictureURL(v1OpenGroup.channel, v1OpenGroup.server) - OpenGroupAPI.leave(v1OpenGroup.channel, v1OpenGroup.server) - // FIXME: No longer supported so let's remove this code - } else if (v2OpenGroup != null) { + if (v2OpenGroup != null) { val apiDB = DatabaseFactory.getLokiAPIDatabase(context) apiDB.removeLastMessageServerID(v2OpenGroup.room, v2OpenGroup.server) apiDB.removeLastDeletionServerID(v2OpenGroup.room, v2OpenGroup.server) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt index d6aead80aa..78b7ced770 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt @@ -29,6 +29,7 @@ import org.session.libsession.utilities.DistributionTypes import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.GroupUtil import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.PublicKeyValidation import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.ConversationActivity @@ -37,7 +38,6 @@ import org.thoughtcrime.securesms.loki.api.OpenGroupManager import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol -import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities import org.thoughtcrime.securesms.loki.viewmodel.DefaultGroupsViewModel import org.thoughtcrime.securesms.loki.viewmodel.State @@ -86,6 +86,9 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode val room = url.pathSegments().firstOrNull() val publicKey = url.queryParameter("public_key") val isV2OpenGroup = !room.isNullOrEmpty() + if (isV2OpenGroup && (publicKey == null || !PublicKeyValidation.isValid(publicKey, 64,false))) { + return Toast.makeText(this, R.string.invalid_public_key, Toast.LENGTH_SHORT).show() + } showLoader() lifecycleScope.launch(Dispatchers.IO) { try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt index 49662194e2..7e4210246b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt @@ -122,7 +122,6 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel } // start polling and wait for updated message ApplicationContext.getInstance(this@LinkDeviceActivity).apply { - setUpStorageAPIIfNeeded() startPollingIfNeeded() } TextSecurePreferences.events.filter { it == TextSecurePreferences.CONFIGURATION_SYNCED }.collect { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt index d5ce931eab..5ddc8b9682 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt @@ -43,6 +43,7 @@ class PNModeActivity : BaseActionBarActivity() { backgroundPollingOptionView.mainColor = resources.getColorWithID(R.color.pn_option_background, theme) backgroundPollingOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme) registerButton.setOnClickListener { register() } + toggleFCM() } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -153,7 +154,6 @@ class PNModeActivity : BaseActionBarActivity() { } TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView)) val application = ApplicationContext.getInstance(this) - application.setUpStorageAPIIfNeeded() application.startPollingIfNeeded() application.registerForFCMIfNeeded(true) val intent = Intent(this, HomeActivity::class.java) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt index c45cb8036c..ac342a707c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt @@ -26,7 +26,6 @@ import nl.komponents.kovenant.all import nl.komponents.kovenant.ui.alwaysUi import nl.komponents.kovenant.ui.successUi import org.session.libsession.avatars.AvatarHelper -import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.utilities.Address import org.session.libsession.utilities.ProfilePictureUtilities import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol @@ -73,7 +72,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) setContentView(R.layout.activity_settings) - val displayName = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) + val displayName = TextSecurePreferences.getProfileName(this) ?: hexEncodedPublicKey glide = GlideApp.with(this) profilePictureView.glide = glide profilePictureView.publicKey = hexEncodedPublicKey @@ -179,8 +178,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { val promises = mutableListOf>() val displayName = displayNameToBeUploaded if (displayName != null) { - val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers() - promises.addAll(servers.map { OpenGroupAPI.setDisplayName(displayName, it) }) TextSecurePreferences.setProfileName(this, displayName) } val profilePicture = profilePictureToBeUploaded @@ -195,7 +192,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt()) TextSecurePreferences.setLastProfilePictureUpload(this, Date().time) ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) - ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded() } if (profilePicture != null || displayName != null) { MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@SettingsActivity) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt index fc3e7c1bba..d070c25f2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt @@ -7,9 +7,9 @@ import androidx.work.* import nl.komponents.kovenant.Promise import nl.komponents.kovenant.all import nl.komponents.kovenant.functional.map +import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.jobs.MessageReceiveJob -import org.session.libsession.messaging.open_groups.OpenGroupV2 -import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller +import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV2 import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.TextSecurePreferences @@ -57,7 +57,10 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor promises.addAll(dmsPromise.get()) // Closed groups - promises.addAll(ClosedGroupPoller().pollOnce()) + val closedGroupPoller = ClosedGroupPollerV2() // Intentionally don't use shared + val storage = MessagingModuleConfiguration.shared.storage + val allGroupPublicKeys = storage.getAllClosedGroupPublicKeys() + allGroupPublicKeys.forEach { closedGroupPoller.poll(it) } // Open Groups val threadDB = DatabaseFactory.getLokiThreadDatabase(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt index 20bc37e553..b20de78bdf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.loki.api import android.content.Context import android.graphics.Bitmap import androidx.annotation.WorkerThread +import okhttp3.HttpUrl import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.open_groups.OpenGroupV2 @@ -19,7 +20,22 @@ object OpenGroupManager { private var pollers = mutableMapOf() // One for each server private var isPolling = false - var isAllCaughtUp = false + val isAllCaughtUp: Boolean + get() { + pollers.values.forEach { poller -> + val jobID = poller.secondToLastJob?.id + jobID?.let { + val storage = MessagingModuleConfiguration.shared.storage + if (storage.getMessageReceiveJob(jobID) == null) { + // If the second to last job is done, it means we are now handling the last job + poller.isCaughtUp = true + poller.secondToLastJob = null + } + } + if (!poller.isCaughtUp) { return false } + } + return true + } fun startPolling() { if (isPolling) { return } @@ -49,8 +65,8 @@ object OpenGroupManager { val existingOpenGroup = threadDB.getOpenGroupChat(threadID) if (existingOpenGroup != null) { return } // Clear any existing data if needed - storage.removeLastDeletionServerId(room, server) - storage.removeLastMessageServerId(room, server) + storage.removeLastDeletionServerID(room, server) + storage.removeLastMessageServerID(room, server) // Store the public key storage.setOpenGroupPublicKey(server,publicKey) // Get an auth token @@ -85,7 +101,8 @@ object OpenGroupManager { val threadDB = DatabaseFactory.getThreadDatabase(context) val openGroupID = "$server.$room" val threadID = GroupManager.getOpenGroupThreadID(openGroupID, context) - val groupID = threadDB.getRecipientForThreadId(threadID)!!.address.serialize() + val recipient = threadDB.getRecipientForThreadId(threadID) ?: return + val groupID = recipient.address.serialize() // Stop the poller if needed val openGroups = storage.getAllV2OpenGroups().filter { it.value.server == server } if (openGroups.count() == 1) { @@ -95,9 +112,22 @@ object OpenGroupManager { } // Delete ThreadUtils.queue { - storage.removeLastDeletionServerId(room, server) - storage.removeLastMessageServerId(room, server) + storage.removeLastDeletionServerID(room, server) + storage.removeLastMessageServerID(room, server) GroupManager.deleteGroup(groupID, context) // Must be invoked on a background thread } } + + fun addOpenGroup(urlAsString: String, context: Context) { + val url = HttpUrl.parse(urlAsString) ?: return + val builder = HttpUrl.Builder().scheme(url.scheme()).host(url.host()) + if (url.port() != 80 || url.port() != 443) { + // Non-standard port; add to server + builder.port(url.port()) + } + val server = builder.build() + val room = url.pathSegments().firstOrNull() ?: return + val publicKey = url.queryParameter("public_key") ?: return + add(server.toString().removeSuffix("/"), room, publicKey, context) + } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt index 7880efbc55..db0e6c5e7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/PublicChatInfoUpdateWorker.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.api import android.content.Context import androidx.work.* -import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities @@ -57,36 +56,16 @@ class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters) override fun doWork(): Result { val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!! - val channel = inputData.getLong(DATA_KEY_CHANNEL, -1) val room = inputData.getString(DATA_KEY_ROOM) - - val isOpenGroupV2 = !room.isNullOrEmpty() && channel == -1L - - if (!isOpenGroupV2) { - val publicChatId = OpenGroup.getId(channel, serverUrl) - - return try { - Log.v(TAG, "Updating open group info for $publicChatId.") - OpenGroupUtilities.updateGroupInfo(context, serverUrl, channel) - Log.v(TAG, "Open group info was successfully updated for $publicChatId.") - Result.success() - } catch (e: Exception) { - Log.e(TAG, "Failed to update open group info for $publicChatId", e) - Result.failure() - } - } else { - val openGroupId = "$serverUrl.$room" - - return try { - Log.v(TAG, "Updating open group info for $openGroupId.") - OpenGroupUtilities.updateGroupInfo(context, serverUrl, room!!) - Log.v(TAG, "Open group info was successfully updated for $openGroupId.") - Result.success() - } catch (e: Exception) { - Log.e(TAG, "Failed to update open group info for $openGroupId", e) - Result.failure() - } - + val openGroupId = "$serverUrl.$room" + return try { + Log.v(TAG, "Updating open group info for $openGroupId.") + OpenGroupUtilities.updateGroupInfo(context, serverUrl, room!!) + Log.v(TAG, "Open group info was successfully updated for $openGroupId.") + Result.success() + } catch (e: Exception) { + Log.e(TAG, "Failed to update open group info for $openGroupId", e) + Result.failure() } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt index 15e76ac0ec..91f04adef8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -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? { val database = databaseHelper.writableDatabase val index = "$server.$room" @@ -294,13 +286,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( }?.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) { val database = databaseHelper.writableDatabase val index = "$server.$room" @@ -308,26 +293,12 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( 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) { val database = databaseHelper.writableDatabase val index = "$server.$room" 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? { val database = databaseHelper.readableDatabase val index = "$server.$room" @@ -336,13 +307,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( }?.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) { val database = databaseHelper.writableDatabase val index = "$server.$room" @@ -392,32 +356,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( 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? { val database = databaseHelper.readableDatabase 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)) } - 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? { val time = TextSecurePreferences.getLastSnodePoolRefreshDate(context) if (time <= 0) { return null } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt index bc2d71f7c6..83e64f884c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt @@ -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? { val database = databaseHelper.readableDatabase return database.get(messageIDTable, "${Companion.messageID} = ?", arrayOf(messageID.toString())) { cursor -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt index c591160c32..b0e1cfe992 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiThreadDatabase.kt @@ -3,17 +3,13 @@ package org.thoughtcrime.securesms.loki.database import android.content.ContentValues import android.content.Context import android.database.Cursor -import org.session.libsession.messaging.open_groups.OpenGroup - import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.loki.utilities.* - import org.session.libsession.messaging.open_groups.OpenGroupV2 import org.session.libsession.utilities.Address import org.session.libsession.utilities.recipients.Recipient - import org.session.libsignal.utilities.JsonUtil class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { @@ -22,7 +18,6 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa private val sessionResetTable = "loki_thread_session_reset_database" val publicChatTable = "loki_public_chat_database" val threadID = "thread_id" - private val friendRequestStatus = "friend_request_status" private val sessionResetStatus = "session_reset_status" val publicChat = "public_chat" @JvmStatic @@ -37,28 +32,6 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) } - fun getAllPublicChats(): Map { - val database = databaseHelper.readableDatabase - var cursor: Cursor? = null - val result = mutableMapOf() - try { - cursor = database.rawQuery("select * from $publicChatTable", null) - while (cursor != null && cursor.moveToNext()) { - val threadID = cursor.getLong(threadID) - val string = cursor.getString(publicChat) - val publicChat = OpenGroup.fromJSON(string) - if (publicChat != null) { - result[threadID] = publicChat - } - } - } catch (e: Exception) { - // Do nothing - } finally { - cursor?.close() - } - return result - } - fun getAllV2OpenGroups(): Map { val database = databaseHelper.readableDatabase var cursor: Cursor? = null @@ -79,20 +52,6 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa return result } - fun getAllPublicChatServers(): Set { - return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) } - } - - fun getPublicChat(threadID: Long): OpenGroup? { - if (threadID < 0) { return null } - - val database = databaseHelper.readableDatabase - return database.get(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString())) { cursor -> - val publicChatAsJSON = cursor.getString(publicChat) - OpenGroup.fromJSON(publicChatAsJSON) - } - } - fun getOpenGroupChat(threadID: Long): OpenGroupV2? { if (threadID < 0) { return null @@ -114,19 +73,4 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa contentValues.put(publicChat, JsonUtil.toJson(openGroupV2.toJson())) database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf(threadID.toString())) } - - fun setPublicChat(publicChat: OpenGroup, threadID: Long) { - if (threadID < 0) { - return - } - val database = databaseHelper.writableDatabase - val contentValues = ContentValues(2) - contentValues.put(Companion.threadID, threadID) - contentValues.put(Companion.publicChat, JsonUtil.toJson(publicChat.toJSON())) - database.insertOrUpdate(publicChatTable, contentValues, "${Companion.threadID} = ?", arrayOf(threadID.toString())) - } - - fun removePublicChat(threadID: Long) { - databaseHelper.writableDatabase.delete(publicChatTable, "${Companion.threadID} = ?", arrayOf(threadID.toString())) - } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt index 3323283514..fba0d64b36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiUserDatabase.kt @@ -1,19 +1,12 @@ package org.thoughtcrime.securesms.loki.database -import android.content.ContentValues 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.helpers.SQLCipherOpenHelper 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.libsignal.database.LokiUserDatabaseProtocol -class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol { +class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { companion object { // 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));" } - override fun getDisplayName(publicKey: String): String? { + fun getDisplayName(publicKey: String): String? { if (publicKey == TextSecurePreferences.getLocalNumber(context)) { return TextSecurePreferences.getProfileName(context) } else { @@ -44,42 +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 getServerDisplayName(serverID: String, publicKey: String): String? { - val database = databaseHelper.readableDatabase - return database.get(serverDisplayNameTable, "${Companion.publicKey} = ? AND ${Companion.serverID} = ?", arrayOf( publicKey, serverID )) { cursor -> - cursor.getString(cursor.getColumnIndexOrThrow(displayName)) - } - } - - fun setServerDisplayName(serverID: String, publicKey: String, displayName: String) { - val database = databaseHelper.writableDatabase - val values = ContentValues(3) - values.put(Companion.serverID, serverID) - values.put(Companion.publicKey, publicKey) - values.put(Companion.displayName, displayName) - try { - database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE) - Recipient.from(context, Address.fromSerialized(publicKey), false).notifyListeners() - } catch (e: Exception) { - Log.d("Loki", "Couldn't save server display name due to exception: $e.") - } - } - - 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 - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt new file mode 100644 index 0000000000..7d5e93b502 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionContactDatabase.kt @@ -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 { + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionJobDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionJobDatabase.kt index 55409e1a5e..a0ca59dd0f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionJobDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/SessionJobDatabase.kt @@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.loki.utilities.* class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { companion object { - private const val sessionJobTable = "session_job_database" + const val sessionJobTable = "session_job_database" const val jobID = "job_id" const val jobType = "job_type" const val failureCount = "failure_count" @@ -71,6 +71,13 @@ class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa } } + fun getMessageReceiveJob(messageReceiveJobID: String): MessageReceiveJob? { + val database = databaseHelper.readableDatabase + return database.get(sessionJobTable, "$jobID = ? AND $jobType = ?", arrayOf( messageReceiveJobID, MessageReceiveJob.KEY )) { cursor -> + jobFromCursor(cursor) as MessageReceiveJob? + } + } + fun cancelPendingMessageSendJobs(threadID: Long) { val database = databaseHelper.writableDatabase val attachmentUploadJobKeys = mutableListOf() diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt deleted file mode 100644 index dff4e5fa30..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationBottomSheet.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.thoughtcrime.securesms.loki.dialogs - -import android.app.AlertDialog -import android.app.Dialog -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.fragment_key_pair_migration_bottom_sheet.* -import network.loki.messenger.R -import org.thoughtcrime.securesms.ApplicationContext - -class KeyPairMigrationBottomSheet : BottomSheetDialogFragment() { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_key_pair_migration_bottom_sheet, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - upgradeNowButton.setOnClickListener { upgradeNow() } - upgradeLaterButton.setOnClickListener { upgradeLater() } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val dialog = super.onCreateDialog(savedInstanceState) - dialog.setOnShowListener { - val d = dialog as BottomSheetDialog - val bottomSheet = d.findViewById(com.google.android.material.R.id.design_bottom_sheet)!! - BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED - BottomSheetBehavior.from(bottomSheet).isHideable = false - } - isCancelable = false - return dialog - } - - private fun upgradeNow() { - val applicationContext = requireContext().applicationContext as ApplicationContext - applicationContext.clearAllData(true) - } - - private fun upgradeLater() { - val dialog = AlertDialog.Builder(requireContext()) - dialog.setMessage("You won't be able to send or receive messages until you upgrade.") - dialog.setPositiveButton(R.string.ok) { _, _ -> - dismiss() - } - dialog.create().show() - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationSuccessBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationSuccessBottomSheet.kt deleted file mode 100644 index 21ab2c5f34..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/KeyPairMigrationSuccessBottomSheet.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.thoughtcrime.securesms.loki.dialogs - -import android.app.AlertDialog -import android.app.Dialog -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.Toast -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.fragment_key_pair_migration_success_bottom_sheet.* -import network.loki.messenger.R -import org.session.libsession.utilities.TextSecurePreferences - -class KeyPairMigrationSuccessBottomSheet : BottomSheetDialogFragment() { - - private val sessionID by lazy { - TextSecurePreferences.getLocalNumber(requireContext()) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_key_pair_migration_success_bottom_sheet, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - sessionIDTextView.text = sessionID - copyButton.setOnClickListener { copySessionID() } - okButton.setOnClickListener { dismiss() } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val dialog = super.onCreateDialog(savedInstanceState) - // Expand the bottom sheet by default - dialog.setOnShowListener { - val d = dialog as BottomSheetDialog - val bottomSheet = d.findViewById(com.google.android.material.R.id.design_bottom_sheet) - BottomSheetBehavior.from(bottomSheet!!).setState(BottomSheetBehavior.STATE_EXPANDED); - } - return dialog - } - - private fun copySessionID() { - val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("Session ID", sessionID) - clipboard.setPrimaryClip(clip) - Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt index 3d884f7440..c020fc3aee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/UserDetailsBottomSheet.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.loki.dialogs +import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager import android.content.Context @@ -8,14 +9,19 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager import android.widget.Toast import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.* -import kotlinx.android.synthetic.main.view_conversation.view.* 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.mms.GlideApp -public class UserDetailsBottomSheet : BottomSheetDialogFragment() { +class UserDetailsBottomSheet : BottomSheetDialogFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 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?) { super.onViewCreated(view, savedInstanceState) val publicKey = arguments?.getString("publicKey") ?: return dismiss() + val recipient = Recipient.from(requireContext(), Address.fromSerialized(publicKey), false) profilePictureView.publicKey = publicKey profilePictureView.glide = GlideApp.with(this) profilePictureView.isLarge = true 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 copyButton.setOnClickListener { 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() } } + + 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) + } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt index c1145c5f49..1f8bdca7ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt @@ -1,15 +1,8 @@ package org.thoughtcrime.securesms.loki.protocol -import android.content.Context -import org.thoughtcrime.securesms.ApplicationContext 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.TextSecurePreferences -import org.session.libsignal.messages.SignalServiceContent import org.session.libsignal.messages.SignalServiceDataMessage -import java.security.MessageDigest object SessionMetaProtocol { @@ -39,43 +32,6 @@ object SessionMetaProtocol { return shouldIgnoreMessage } - @JvmStatic - fun shouldIgnoreDecryptionException(context: Context, timestamp: Long): Boolean { - val restorationTimestamp = TextSecurePreferences.getRestorationTime(context) - return timestamp <= restorationTimestamp - } - - @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 - fun handleProfileKeyUpdate(context: Context, content: SignalServiceContent) { - val message = content.dataMessage.get() - if (!message.profileKey.isPresent) { return } - val database = DatabaseFactory.getRecipientDatabase(context) - val recipient = Recipient.from(context, Address.fromSerialized(content.sender), false) - if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, message.profileKey.get())) { - database.setProfileKey(recipient, message.profileKey.get()) - database.setUnidentifiedAccessMode(recipient, Recipient.UnidentifiedAccessMode.UNKNOWN) - val url = content.senderProfilePictureURL.or("") - ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(recipient, url)) - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - if (userPublicKey == content.sender) { - ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() - } - } - } - @JvmStatic fun canUserReplyToNotification(recipient: Recipient): Boolean { // TODO return !recipient.address.isRSSFeed diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt index 140586930f..23834fb9af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/DatabaseUtilities.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.loki.utilities import android.content.ContentValues +import androidx.core.database.getStringOrNull import net.sqlcipher.Cursor import net.sqlcipher.database.SQLiteDatabase import org.session.libsignal.utilities.Base64 @@ -56,4 +57,8 @@ fun Cursor.getLong(columnName: String): Long { fun Cursor.getBase64EncodedData(columnName: String): ByteArray { return Base64.decode(getString(columnName)) +} + +fun Cursor.getStringOrNull(columnName: String): String? { + return getStringOrNull(getColumnIndexOrThrow(columnName)) } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt index faef8f0cc3..000b61db89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionManagerUtilities.kt @@ -15,7 +15,7 @@ object MentionManagerUtilities { val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() } result.addAll(members) } else { - if (MentionsManager.shared.userPublicKeyCache[threadID] != null) { return } + if (MentionsManager.userPublicKeyCache[threadID] != null) { return } val messageDatabase = DatabaseFactory.getMmsSmsDatabase(context) val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID)) var record: MessageRecord? = reader.next @@ -30,6 +30,6 @@ object MentionManagerUtilities { reader.close() result.add(TextSecurePreferences.getLocalNumber(context)!!) } - MentionsManager.shared.userPublicKeyCache[threadID] = result + MentionsManager.userPublicKeyCache[threadID] = result } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt index 50b69274da..45fda301d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt @@ -9,6 +9,7 @@ import android.text.style.StyleSpan import android.util.Range import network.loki.messenger.R import nl.komponents.kovenant.combine.Tuple2 +import org.session.libsession.messaging.contacts.Contact import org.thoughtcrime.securesms.database.DatabaseFactory import org.session.libsession.utilities.TextSecurePreferences import java.util.regex.Pattern @@ -23,21 +24,22 @@ object MentionUtilities { @JvmStatic fun highlightMentions(text: CharSequence, isOutgoingMessage: Boolean, threadID: Long, context: Context): SpannableString { var text = text + val threadDB = DatabaseFactory.getThreadDatabase(context) + val isOpenGroup = threadDB.getRecipientForThreadId(threadID)?.isOpenGroupRecipient ?: false val pattern = Pattern.compile("@[0-9a-fA-F]*") var matcher = pattern.matcher(text) val mentions = mutableListOf, String>>() var startIndex = 0 - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) val userPublicKey = TextSecurePreferences.getLocalNumber(context)!! if (matcher.find(startIndex)) { while (true) { 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) - } else if (publicChat != null) { - DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey) } 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) { text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt deleted file mode 100644 index 7db784d8ca..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/NotificationUtilities.kt +++ /dev/null @@ -1,18 +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 threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(threadRecipient) - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - val publicKey = recipient.address.toString() - val displayName = if (publicChat != null) { - DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey) - } else { - DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) - } - return displayName ?: publicKey -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt index 8c08d53e3b..2f562e87c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt @@ -3,18 +3,10 @@ package org.thoughtcrime.securesms.loki.utilities import android.content.Context import androidx.annotation.WorkerThread import org.greenrobot.eventbus.EventBus -import org.session.libsession.messaging.MessagingModuleConfiguration -import org.session.libsession.messaging.open_groups.OpenGroup -import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 -import org.session.libsession.messaging.open_groups.OpenGroupV2 import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.GroupUtil -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.ProfileKeyUtil -import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.groups.GroupManager import java.util.* //TODO Refactor so methods declare specific type of checked exceptions and not generalized Exception. @@ -28,23 +20,6 @@ object OpenGroupUtilities { * * Consider using [org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker] for lazy approach. */ - @JvmStatic - @WorkerThread - @Throws(Exception::class) - fun updateGroupInfo(context: Context, url: String, channel: Long) { - // Check if open group has a related DB record. - val groupId = GroupUtil.getEncodedOpenGroupID(OpenGroup.getId(channel, url).toByteArray()) - if (!DatabaseFactory.getGroupDatabase(context).hasGroup(groupId)) { - throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId") - } - - val info = OpenGroupAPI.getChannelInfo(channel, url).get() - - OpenGroupAPI.updateProfileIfNeeded(channel, url, groupId, info, false) - - EventBus.getDefault().post(GroupInfoUpdatedEvent(url, channel)) - } - @JvmStatic @WorkerThread @Throws(Exception::class) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt index e1a308b645..efbaa24dbd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt @@ -2,13 +2,15 @@ package org.thoughtcrime.securesms.loki.views import android.content.Context import android.graphics.Typeface -import android.text.TextUtils import android.util.AttributeSet import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout import kotlinx.android.synthetic.main.view_conversation.view.* 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.model.ThreadRecord import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded @@ -56,7 +58,7 @@ class ConversationView : LinearLayout { } profilePictureView.glide = glide 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 timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date) muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE @@ -85,9 +87,12 @@ class ConversationView : LinearLayout { profilePictureView.recycle() } - private fun getUserDisplayName(publicKey: String?): String? { - if (TextUtils.isEmpty(publicKey)) return null - return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!) + private fun getUserDisplayName(recipient: Recipient): String? { + if (recipient.isLocalNumber) { + return context.getString(R.string.note_to_self) + } else { + return recipient.name // Internally uses the Contact API + } } // endregion } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt index d702610668..b7745a67a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt @@ -17,10 +17,10 @@ class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defS set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue } var glide: GlideRequests? = null set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue } - var publicChatServer: String? = null - set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.publicChatServer = publicChatServer } - var publicChatChannel: Long? = null - set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.publicChatChannel = publicChatChannel } + var openGroupServer: String? = null + set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.openGroupServer = openGroupServer } + var openGroupRoom: String? = null + set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.openGroupRoom = openGroupRoom } var onMentionCandidateSelected: ((Mention) -> Unit)? = null private val mentionCandidateSelectionViewAdapter by lazy { Adapter(context) } @@ -29,8 +29,8 @@ class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defS var mentionCandidates = listOf() set(newValue) { field = newValue; notifyDataSetChanged() } var glide: GlideRequests? = null - var publicChatServer: String? = null - var publicChatChannel: Long? = null + var openGroupServer: String? = null + var openGroupRoom: String? = null override fun getCount(): Int { return mentionCandidates.count() @@ -49,8 +49,8 @@ class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defS val mentionCandidate = getItem(position) cell.glide = glide cell.mentionCandidate = mentionCandidate - cell.publicChatServer = publicChatServer - cell.publicChatChannel = publicChatChannel + cell.openGroupServer = openGroupServer + cell.openGroupRoom = openGroupRoom return cell } } @@ -68,10 +68,10 @@ class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defS } fun show(mentionCandidates: List, threadID: Long) { - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - if (publicChat != null) { - publicChatServer = publicChat.server - publicChatChannel = publicChat.channel + val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID) + if (openGroup != null) { + openGroupServer = openGroup.server + openGroupRoom = openGroup.room } this.mentionCandidates = mentionCandidates val layoutParams = this.layoutParams as ViewGroup.LayoutParams diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt index 07233c3e06..0a748c98f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt @@ -8,16 +8,16 @@ import android.view.ViewGroup import android.widget.LinearLayout import kotlinx.android.synthetic.main.view_mention_candidate.view.* import network.loki.messenger.R -import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.messaging.mentions.Mention +import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.thoughtcrime.securesms.mms.GlideRequests class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) { var mentionCandidate = Mention("", "") set(newValue) { field = newValue; update() } var glide: GlideRequests? = null - var publicChatServer: String? = null - var publicChatChannel: Long? = null + var openGroupServer: String? = null + var openGroupRoom: String? = null constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) constructor(context: Context) : this(context, null) @@ -37,8 +37,8 @@ class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: profilePictureView.isRSSFeed = false profilePictureView.glide = glide!! profilePictureView.update() - if (publicChatServer != null && publicChatChannel != null) { - val isUserModerator = OpenGroupAPI.isUserModerator(mentionCandidate.publicKey, publicChatChannel!!, publicChatServer!!) + if (openGroupServer != null && openGroupRoom != null) { + val isUserModerator = OpenGroupAPIV2.isUserModerator(mentionCandidate.publicKey, openGroupRoom!!, openGroupServer!!) moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE } else { moderatorIconImageView.visibility = View.GONE diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt index fbc469379e..e2de25ca90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt @@ -11,6 +11,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import kotlinx.android.synthetic.main.view_profile_picture.view.* import network.loki.messenger.R import org.session.libsession.avatars.ProfileContactPhoto +import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.mentions.MentionsManager import org.session.libsession.utilities.Address import org.session.libsession.utilities.recipients.Recipient @@ -29,7 +30,7 @@ class ProfilePictureView : RelativeLayout { var additionalDisplayName: String? = null var isRSSFeed = false var isLarge = false - private val imagesCached = mutableSetOf() + private val profilePicturesCached = mutableMapOf() // region Lifecycle constructor(context: Context) : super(context) { @@ -57,23 +58,15 @@ class ProfilePictureView : RelativeLayout { // region Updating fun update(recipient: Recipient, threadID: Long) { - fun getUserDisplayName(publicKey: String?): String? { - if (publicKey == null || publicKey.isBlank()) { - return null - } else { - var result = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - if (result == null && publicChat != null) { - result = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey) - } - return result ?: publicKey - } + fun getUserDisplayName(publicKey: String): String { + val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey) + return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey } fun isOpenGroupWithProfilePicture(recipient: Recipient): Boolean { return recipient.isOpenGroupRecipient && recipient.groupAvatarId != null } 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)) val randomUsers = users.sorted().toMutableList() // Sort to provide a level of stability if (users.count() == 1) { @@ -90,7 +83,8 @@ class ProfilePictureView : RelativeLayout { recipient.name == "Session Updates" || recipient.name == "Session Public Chat" } else { - publicKey = recipient.address.toString() + val publicKey = recipient.address.toString() + this.publicKey = publicKey displayName = getUserDisplayName(publicKey) additionalPublicKey = null isRSSFeed = false @@ -146,13 +140,13 @@ class ProfilePictureView : RelativeLayout { private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) { if (publicKey.isNotEmpty()) { val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false) - if (imagesCached.contains(publicKey)) return + if (profilePicturesCached.containsKey(publicKey) && profilePicturesCached[publicKey] == recipient.profileAvatar) return val signalProfilePicture = recipient.contactPhoto - if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" - && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") { + val avatar = (signalProfilePicture as? ProfileContactPhoto)?.avatarObject + if (signalProfilePicture != null && avatar != "0" && avatar != "") { glide.clear(imageView) glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(imageView) - imagesCached.add(publicKey) + profilePicturesCached[publicKey] = recipient.profileAvatar } else { val sizeInPX = resources.getDimensionPixelSize(sizeResId) glide.clear(imageView) @@ -162,7 +156,7 @@ class ProfilePictureView : RelativeLayout { publicKey, displayName )).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) - imagesCached.add(publicKey) + profilePicturesCached[publicKey] = recipient.profileAvatar } } else { imageView.setImageDrawable(null) @@ -170,7 +164,7 @@ class ProfilePictureView : RelativeLayout { } fun recycle() { - imagesCached.clear() + profilePicturesCached.clear() } // endregion -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt index 193bd1bd29..7dae953974 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt @@ -8,6 +8,7 @@ import android.widget.LinearLayout import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView import kotlinx.android.synthetic.main.view_user.view.* import network.loki.messenger.R +import org.session.libsession.messaging.contacts.Contact import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities import org.thoughtcrime.securesms.mms.GlideRequests @@ -48,17 +49,9 @@ class UserView : LinearLayout { // region Updating fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) { - fun getUserDisplayName(publicKey: String?): String? { - if (publicKey == null || publicKey.isBlank()) { - return null - } else { - var result = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey) - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(openGroupThreadID) - if (result == null && publicChat != null) { - result = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey) - } - return result ?: publicKey - } + fun getUserDisplayName(publicKey: String): String { + val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey) + return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey } val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(user) MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadID, context) // FIXME: This is a bad place to do this diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java index 12fc8279f0..f71050aba9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PushMediaConstraints.java @@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; -import org.session.libsession.messaging.file_server.FileServerAPI; +import org.session.libsession.messaging.file_server.FileServerAPIV2; public class PushMediaConstraints extends MediaConstraints { @@ -21,26 +21,26 @@ public class PushMediaConstraints extends MediaConstraints { @Override public int getImageMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + return (int) (((double) FileServerAPIV2.maxFileSize) / FileServerAPIV2.fileSizeORMultiplier); } @Override public int getGifMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + return (int) (((double) FileServerAPIV2.maxFileSize) / FileServerAPIV2.fileSizeORMultiplier); } @Override public int getVideoMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + return (int) (((double) FileServerAPIV2.maxFileSize) / FileServerAPIV2.fileSizeORMultiplier); } @Override public int getAudioMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + return (int) (((double) FileServerAPIV2.maxFileSize) / FileServerAPIV2.fileSizeORMultiplier); } @Override public int getDocumentMaxSize(Context context) { - return (int) (((double) FileServerAPI.Companion.getMaxFileSize()) / FileServerAPI.Companion.getFileSizeORMultiplier()); + return (int) (((double) FileServerAPIV2.maxFileSize) / FileServerAPIV2.fileSizeORMultiplier); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java index 55dd3f17a2..532c953152 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -287,9 +287,6 @@ public class DefaultMessageNotifier implements MessageNotifier { } finally { if (telcoCursor != null) telcoCursor.close(); if (pushCursor != null) pushCursor.close(); - if (!OpenGroupManager.INSTANCE.isAllCaughtUp()) { - OpenGroupManager.INSTANCE.setAllCaughtUp(true); - } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java index 9527282d39..f094cea12c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java @@ -8,14 +8,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; 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.utilities.NotificationUtilities; +import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; import org.session.libsession.utilities.NotificationPrivacyPreference; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; - import java.util.LinkedList; import java.util.List; @@ -49,8 +49,15 @@ public class MultipleRecipientNotificationBuilder extends AbstractNotificationBu public void setMostRecentSender(Recipient recipient, Recipient threadRecipient) { String displayName = recipient.toShortString(); - if (threadRecipient.isGroupRecipient()) { - displayName = NotificationUtilities.getOpenGroupDisplayName(recipient, threadRecipient, context); + if (threadRecipient.isOpenGroupRecipient()) { + 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()) { 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) { String displayName = sender.toShortString(); - if (threadRecipient.isGroupRecipient()) { - displayName = NotificationUtilities.getOpenGroupDisplayName(sender, threadRecipient, context); + if (threadRecipient.isOpenGroupRecipient()) { + 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()) { SpannableStringBuilder builder = new SpannableStringBuilder(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java index 448d977da9..aac737add2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -10,6 +10,7 @@ import org.session.libsession.messaging.sending_receiving.notifications.MessageN import org.session.libsession.messaging.sending_receiving.pollers.Poller; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.Debouncer; +import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.ThreadUtils; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.loki.api.OpenGroupManager; @@ -23,7 +24,7 @@ public class OptimizedMessageNotifier implements MessageNotifier { @MainThread public OptimizedMessageNotifier(@NonNull MessageNotifier wrapped) { this.wrapped = wrapped; - this.debouncer = new Debouncer(TimeUnit.SECONDS.toMillis(1)); + this.debouncer = new Debouncer(TimeUnit.SECONDS.toMillis(2)); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 0ed628034f..008779301b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -15,22 +15,21 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.text.SpannableStringBuilder; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat.Action; import androidx.core.app.RemoteInput; - import com.bumptech.glide.load.engine.DiskCacheStrategy; - import org.session.libsession.avatars.ContactColors; import org.session.libsession.avatars.ContactPhoto; import org.session.libsession.avatars.GeneratedContactPhoto; +import org.session.libsession.messaging.contacts.Contact; 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.NotificationUtilities; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; import org.thoughtcrime.securesms.mms.GlideApp; 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.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; - import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutionException; - import network.loki.messenger.R; public class SingleRecipientNotificationBuilder extends AbstractNotificationBuilder { @@ -121,8 +118,16 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); - if (privacy.isDisplayContact() && threadRecipients.isGroupRecipient()) { - String displayName = NotificationUtilities.getOpenGroupDisplayName(individualRecipient, threadRecipients, context); + if (privacy.isDisplayContact() && threadRecipients.isOpenGroupRecipient()) { + 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) { stringBuilder.append(Util.getBoldedString(displayName + ": ")); } @@ -209,8 +214,16 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); - if (privacy.isDisplayContact() && threadRecipient.isGroupRecipient()) { - String displayName = NotificationUtilities.getOpenGroupDisplayName(individualRecipient, threadRecipient, context); + if (privacy.isDisplayContact() && threadRecipient.isOpenGroupRecipient()) { + 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) { stringBuilder.append(Util.getBoldedString(displayName + ": ")); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt index ae98e00cbb..e7fdbc509f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt @@ -1,22 +1,70 @@ package org.thoughtcrime.securesms.sskenvironment import android.content.Context +import org.session.libsession.messaging.contacts.Contact import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.SSKEnvironment import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob -class ProfileManager: SSKEnvironment.ProfileManagerProtocol { - override fun setDisplayName(context: Context, recipient: Recipient, displayName: String) { - DatabaseFactory.getLokiUserDatabase(context).setDisplayName(recipient.address.serialize(), displayName) +class ProfileManager : SSKEnvironment.ProfileManagerProtocol { + + 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) { - 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) { + // 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) database.setProfileKey(recipient, profileKey) } @@ -25,8 +73,4 @@ class ProfileManager: SSKEnvironment.ProfileManagerProtocol { val database = DatabaseFactory.getRecipientDatabase(context) database.setUnidentifiedAccessMode(recipient, unidentifiedAccessMode) } - - override fun updateOpenGroupProfilePicturesIfNeeded(context: Context) { - ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded() - } } \ No newline at end of file diff --git a/app/src/main/res/layout-sw400dp/activity_landing.xml b/app/src/main/res/layout-sw400dp/activity_landing.xml index 52906283a7..dc36e5fa57 100644 --- a/app/src/main/res/layout-sw400dp/activity_landing.xml +++ b/app/src/main/res/layout-sw400dp/activity_landing.xml @@ -59,6 +59,6 @@ android:background="@color/transparent" android:textAllCaps="false" android:textSize="@dimen/medium_font_size" - android:text="Link a Device" /> + android:text="@string/activity_link_device_link_device" /> \ No newline at end of file diff --git a/app/src/main/res/layout-sw400dp/activity_pn_mode.xml b/app/src/main/res/layout-sw400dp/activity_pn_mode.xml index fb1fc1c6ca..71cfc81180 100644 --- a/app/src/main/res/layout-sw400dp/activity_pn_mode.xml +++ b/app/src/main/res/layout-sw400dp/activity_pn_mode.xml @@ -19,7 +19,7 @@ android:textSize="@dimen/very_large_font_size" android:textStyle="bold" android:textColor="@color/text" - android:text="Message Notifications" /> + android:text="@string/activity_pn_mode_message_notifications" /> + android:text="@string/activity_pn_mode_explanation" /> + android:text="@string/activity_pn_mode_fast_mode" /> + android:text="@string/activity_pn_mode_fast_mode_explanation" /> + android:text="@string/activity_pn_mode_slow_mode" /> + android:text="@string/activity_pn_mode_slow_mode_explanation" /> diff --git a/app/src/main/res/layout-sw400dp/activity_recovery_phrase_restore.xml b/app/src/main/res/layout-sw400dp/activity_recovery_phrase_restore.xml index 6f3535ede0..b88592a68c 100644 --- a/app/src/main/res/layout-sw400dp/activity_recovery_phrase_restore.xml +++ b/app/src/main/res/layout-sw400dp/activity_recovery_phrase_restore.xml @@ -1,6 +1,7 @@ @@ -34,7 +35,7 @@ style="@style/SessionEditText" android:id="@+id/mnemonicEditText" android:layout_width="match_parent" - android:layout_height="80dp" + android:layout_height="64dp" android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginTop="12dp" android:layout_marginRight="@dimen/very_large_spacing" @@ -42,7 +43,7 @@ android:paddingBottom="0dp" android:gravity="center_vertical" android:inputType="textMultiLine" - android:maxLines="2" + android:maxLines="3" android:hint="@string/activity_restore_seed_edit_text_hint" /> + android:text="By using this service, you agree to our Terms of Service and Privacy Policy" + tools:ignore="HardcodedText" /> \ No newline at end of file diff --git a/app/src/main/res/layout-sw400dp/activity_register.xml b/app/src/main/res/layout-sw400dp/activity_register.xml index 8cc432c160..76e4e3e749 100644 --- a/app/src/main/res/layout-sw400dp/activity_register.xml +++ b/app/src/main/res/layout-sw400dp/activity_register.xml @@ -1,6 +1,7 @@ @@ -38,7 +39,7 @@ android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginTop="@dimen/medium_spacing" android:layout_marginRight="@dimen/very_large_spacing" - android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> + tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> + android:text="By using this service, you agree to our Terms of Service and Privacy Policy" + tools:ignore="HardcodedText" /> \ No newline at end of file diff --git a/app/src/main/res/layout-sw400dp/activity_seed.xml b/app/src/main/res/layout-sw400dp/activity_seed.xml index 74f44a79d4..3592134df9 100644 --- a/app/src/main/res/layout-sw400dp/activity_seed.xml +++ b/app/src/main/res/layout-sw400dp/activity_seed.xml @@ -1,8 +1,8 @@ - @@ -47,7 +47,7 @@ android:gravity="center" android:textSize="16sp" android:textAlignment="center" - android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" /> + tools:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" /> + android:text="@string/fragment_recovery_phrase_title" /> + android:text="@string/activity_restore_explanation" /> + android:text="@string/view_seed_reminder_title" /> + android:text="@string/view_seed_reminder_subtitle_1" /> @@ -64,7 +64,7 @@ android:layout_height="28dp" android:layout_marginLeft="4dp" android:textStyle="normal" - android:text="Continue" /> + android:text="@string/continue_2" /> diff --git a/app/src/main/res/layout/activity_backup_restore.xml b/app/src/main/res/layout/activity_backup_restore.xml index 365ff39cd1..af86967c77 100644 --- a/app/src/main/res/layout/activity_backup_restore.xml +++ b/app/src/main/res/layout/activity_backup_restore.xml @@ -99,7 +99,8 @@ android:text="By using this service, you agree to our Terms of Service and Privacy Policy" android:textColor="@color/text" android:textColorLink="@color/text" - android:textSize="@dimen/very_small_font_size" /> + android:textSize="@dimen/very_small_font_size" + tools:ignore="HardcodedText" /> diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 49b5d36053..a732ad3a59 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -42,7 +42,7 @@ android:layout_centerVertical="true" android:layout_marginLeft="64dp" android:fontFamily="sans-serif-medium" - android:text="Session" + android:text="@string/app_name" android:textColor="@color/text" android:textSize="@dimen/very_large_font_size" /> diff --git a/app/src/main/res/layout/activity_landing.xml b/app/src/main/res/layout/activity_landing.xml index b2b4c3cde6..2858913c18 100644 --- a/app/src/main/res/layout/activity_landing.xml +++ b/app/src/main/res/layout/activity_landing.xml @@ -59,6 +59,6 @@ android:background="@color/transparent" android:textAllCaps="false" android:textSize="@dimen/medium_font_size" - android:text="Link a Device" /> + android:text="@string/activity_link_device_link_device" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_open_group_guidelines.xml b/app/src/main/res/layout/activity_open_group_guidelines.xml index 34d03a8f89..4105e48187 100644 --- a/app/src/main/res/layout/activity_open_group_guidelines.xml +++ b/app/src/main/res/layout/activity_open_group_guidelines.xml @@ -18,7 +18,7 @@ android:textSize="@dimen/large_font_size" android:textStyle="bold" android:textColor="@color/text" - android:text="Community Guidelines" /> + android:text="@string/ConversationActivity_open_group_guidelines" /> + android:text="@string/activity_pn_mode_message_notifications" /> + android:text="@string/activity_pn_mode_explanation" /> + android:text="@string/activity_pn_mode_fast_mode" /> + android:text="@string/activity_pn_mode_fast_mode_explanation" /> + android:text="@string/activity_pn_mode_slow_mode" /> + android:text="@string/activity_pn_mode_slow_mode_explanation" /> @@ -104,7 +104,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/medium_button_height" android:layout_marginLeft="@dimen/massive_spacing" - android:layout_marginTop="@dimen/very_large_spacing" + android:layout_marginTop="20dp" android:layout_marginRight="@dimen/massive_spacing" android:layout_marginBottom="@dimen/onboarding_button_bottom_offset" android:text="@string/continue_2" /> diff --git a/app/src/main/res/layout/activity_recovery_phrase_restore.xml b/app/src/main/res/layout/activity_recovery_phrase_restore.xml index 5fede38026..188c592e97 100644 --- a/app/src/main/res/layout/activity_recovery_phrase_restore.xml +++ b/app/src/main/res/layout/activity_recovery_phrase_restore.xml @@ -1,6 +1,7 @@ @@ -34,7 +35,7 @@ style="@style/SmallSessionEditText" android:id="@+id/mnemonicEditText" android:layout_width="match_parent" - android:layout_height="80dp" + android:layout_height="64dp" android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginTop="10dp" android:layout_marginRight="@dimen/very_large_spacing" @@ -42,7 +43,7 @@ android:paddingBottom="0dp" android:gravity="center_vertical" android:inputType="textMultiLine" - android:maxLines="2" + android:maxLines="3" android:hint="@string/activity_restore_seed_edit_text_hint" /> + android:text="By using this service, you agree to our Terms of Service and Privacy Policy" + tools:ignore="HardcodedText" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_register.xml b/app/src/main/res/layout/activity_register.xml index 57a64e2fb3..0f0d560b86 100644 --- a/app/src/main/res/layout/activity_register.xml +++ b/app/src/main/res/layout/activity_register.xml @@ -1,6 +1,7 @@ @@ -35,11 +36,11 @@ android:id="@+id/publicKeyTextView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:textSize="18dp" + android:textSize="18sp" android:layout_marginLeft="@dimen/very_large_spacing" android:layout_marginTop="10dp" android:layout_marginRight="@dimen/very_large_spacing" - android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> + tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> + android:text="By using this service, you agree to our Terms of Service and Privacy Policy" + tools:ignore="HardcodedText" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_seed.xml b/app/src/main/res/layout/activity_seed.xml index 8aacbf3df3..60a61b7860 100644 --- a/app/src/main/res/layout/activity_seed.xml +++ b/app/src/main/res/layout/activity_seed.xml @@ -1,8 +1,8 @@ - @@ -47,7 +47,7 @@ android:gravity="center" android:textSize="14sp" android:textAlignment="center" - android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" /> + tools:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" /> + tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> + android:text="@string/activity_settings_help_translate_session" /> diff --git a/app/src/main/res/layout/conversation_activity.xml b/app/src/main/res/layout/conversation_activity.xml index ba9f7f27e1..e920d5f540 100644 --- a/app/src/main/res/layout/conversation_activity.xml +++ b/app/src/main/res/layout/conversation_activity.xml @@ -55,9 +55,10 @@ android:id="@+id/titleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:background="@null" android:maxLines="1" android:ellipsize="end" - android:text="Conversation" + tools:text="Conversation" android:textColor="@color/text" android:textSize="@dimen/large_font_size" android:fontFamily="sans-serif-medium" /> @@ -83,7 +84,7 @@ android:layout_height="wrap_content" android:maxLines="1" android:ellipsize="end" - android:text="26 members" + tools:text="26 members" android:textColor="@color/text" android:textSize="@dimen/small_font_size" /> @@ -217,7 +218,7 @@ android:layout_height="wrap_content" android:background="?android:attr/windowBackground" android:paddingStart="5dip" - android:text="160/160 (1)" + tools:text="160/160 (1)" android:visibility="gone" /> - diff --git a/app/src/main/res/layout/fragment_enter_public_key.xml b/app/src/main/res/layout/fragment_enter_public_key.xml index ffd53dd34e..1caef0c4bc 100644 --- a/app/src/main/res/layout/fragment_enter_public_key.xml +++ b/app/src/main/res/layout/fragment_enter_public_key.xml @@ -1,6 +1,6 @@ - + tools:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> - - - - - - - - - - -