kopia lustrzana https://github.com/ryukoposting/Signal-Android
Move mms and security checks into ViewModel/Repository.
rodzic
c5f4a9c89e
commit
b696a0f758
|
@ -328,6 +328,10 @@ import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Flowable;
|
||||||
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -454,7 +458,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
private boolean isSecureText;
|
private boolean isSecureText;
|
||||||
private boolean isDefaultSms;
|
private boolean isDefaultSms;
|
||||||
private int reactWithAnyEmojiStartPage = -1;
|
private int reactWithAnyEmojiStartPage = -1;
|
||||||
private boolean isMmsEnabled = true;
|
|
||||||
private boolean isSecurityInitialized = false;
|
private boolean isSecurityInitialized = false;
|
||||||
private boolean isSearchRequested = false;
|
private boolean isSearchRequested = false;
|
||||||
private boolean hasProcessedShareData = false;
|
private boolean hasProcessedShareData = false;
|
||||||
|
@ -528,8 +531,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
.commitNow();
|
.commitNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean typingIndicatorsEnabled = TextSecurePreferences.isTypingIndicatorsEnabled(requireContext());
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
hasProcessedShareData = savedInstanceState.getBoolean("SHARED", false);
|
hasProcessedShareData = savedInstanceState.getBoolean("SHARED", false);
|
||||||
}
|
}
|
||||||
|
@ -549,45 +550,14 @@ public class ConversationParentFragment extends Fragment
|
||||||
initializeEnabledCheck();
|
initializeEnabledCheck();
|
||||||
initializePendingRequestsBanner();
|
initializePendingRequestsBanner();
|
||||||
initializeGroupV1MigrationsBanners();
|
initializeGroupV1MigrationsBanners();
|
||||||
initializeSecurity(recipient.get().isRegistered(), isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Boolean result) {
|
|
||||||
if (getActivity() == null) {
|
|
||||||
Log.w(TAG, "Activity is not attached. Not proceeding with initialization.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requireActivity().isFinishing()) {
|
Flowable<ConversationSecurityInfo> observableSecurityInfo = viewModel.getConversationState()
|
||||||
Log.w(TAG, "Activity is finishing. Not proceeding with initialization.");
|
.map(ConversationState::getSecurityInfo)
|
||||||
return;
|
.filter(ConversationSecurityInfo::isInitialized);
|
||||||
}
|
|
||||||
|
|
||||||
initializeProfiles();
|
disposables.add(observableSecurityInfo.subscribe(securityInfo -> handleSecurityChange(securityInfo.isPushAvailable(), securityInfo.isDefaultSmsApplication())));
|
||||||
initializeGv1Migration();
|
disposables.add(viewModel.getConversationSecurityInfo(args.getRecipientId()).firstOrError().subscribe(unused -> onInitialSecurityConfigurationLoaded()));
|
||||||
|
|
||||||
Log.d(TAG, "Initializing data from onViewCreated...");
|
|
||||||
initializeDraft(args).addListener(new AssertedSuccessListener<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Boolean loadedDraft) {
|
|
||||||
if (loadedDraft != null && loadedDraft) {
|
|
||||||
Log.i(TAG, "Finished loading draft");
|
|
||||||
ThreadUtil.runOnMain(() -> {
|
|
||||||
if (fragment != null && fragment.isResumed()) {
|
|
||||||
fragment.moveToLastSeen();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Wanted to move to the last seen position, but the fragment was in an invalid state");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typingIndicatorsEnabled) {
|
|
||||||
composeText.addTextChangedListener(typingTextWatcher);
|
|
||||||
}
|
|
||||||
composeText.setSelection(composeText.length(), composeText.length());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
initializeInsightObserver();
|
initializeInsightObserver();
|
||||||
initializeActionBar();
|
initializeActionBar();
|
||||||
|
|
||||||
|
@ -616,7 +586,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
WindowUtil.setLightStatusBarFromTheme(requireActivity());
|
WindowUtil.setLightStatusBarFromTheme(requireActivity());
|
||||||
|
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
initializeMmsEnabledCheck();
|
viewModel.checkIfMmsIsEnabled();
|
||||||
initializeIdentityRecords();
|
initializeIdentityRecords();
|
||||||
composeText.setMessageSendType(sendButton.getSelectedSendType());
|
composeText.setMessageSendType(sendButton.getSelectedSendType());
|
||||||
|
|
||||||
|
@ -758,7 +728,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
attachmentManager.setLocation(place, getCurrentMediaConstraints());
|
attachmentManager.setLocation(place, getCurrentMediaConstraints());
|
||||||
break;
|
break;
|
||||||
case SMS_DEFAULT:
|
case SMS_DEFAULT:
|
||||||
initializeSecurity(isSecureText, isDefaultSms);
|
viewModel.updateSecurityInfo();
|
||||||
break;
|
break;
|
||||||
case PICK_GIF:
|
case PICK_GIF:
|
||||||
case MEDIA_SENDER:
|
case MEDIA_SENDER:
|
||||||
|
@ -845,6 +815,38 @@ public class ConversationParentFragment extends Fragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onInitialSecurityConfigurationLoaded() {
|
||||||
|
Log.d(TAG, "Initial security configuration loaded.");
|
||||||
|
if (isDetached()) {
|
||||||
|
Log.w(TAG, "Fragment has become detached. Ignoring configuration call.");
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeProfiles();
|
||||||
|
initializeGv1Migration();
|
||||||
|
|
||||||
|
Log.d(TAG, "Initializing draft from initial security configuration load...");
|
||||||
|
initializeDraft(viewModel.getArgs()).addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean loadedDraft) {
|
||||||
|
if (loadedDraft != null && loadedDraft) {
|
||||||
|
Log.i(TAG, "Finished loading draft");
|
||||||
|
ThreadUtil.runOnMain(() -> {
|
||||||
|
if (fragment != null && fragment.isResumed()) {
|
||||||
|
fragment.moveToLastSeen();
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Wanted to move to the last seen position, but the fragment was in an invalid state");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextSecurePreferences.isTypingIndicatorsEnabled(requireContext())) {
|
||||||
|
composeText.addTextChangedListener(typingTextWatcher);
|
||||||
|
}
|
||||||
|
composeText.setSelection(composeText.length(), composeText.length());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void setVisibleThread(long threadId) {
|
private void setVisibleThread(long threadId) {
|
||||||
if (!isInBubble()) {
|
if (!isInBubble()) {
|
||||||
// TODO [alex] LargeScreenSupport -- Inform MainActivityViewModel that the conversation was opened.
|
// TODO [alex] LargeScreenSupport -- Inform MainActivityViewModel that the conversation was opened.
|
||||||
|
@ -1489,7 +1491,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAddAttachment() {
|
private void handleAddAttachment() {
|
||||||
if (this.isMmsEnabled || isSecureText) {
|
if (viewModel.getConversationStateSnapshot().isMmsEnabled() || isSecureText) {
|
||||||
viewModel.getRecentMedia().removeObservers(this);
|
viewModel.getRecentMedia().removeObservers(this);
|
||||||
|
|
||||||
if (attachmentKeyboardStub.resolved() && container.isInputOpen() && container.getCurrentInput() == attachmentKeyboardStub.get()) {
|
if (attachmentKeyboardStub.resolved() && container.isInputOpen() && container.getCurrentInput() == attachmentKeyboardStub.get()) {
|
||||||
|
@ -1598,6 +1600,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
calculateCharactersRemaining();
|
calculateCharactersRemaining();
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
setBlockedUserState(recipient.get(), isSecureText, isDefaultSms);
|
setBlockedUserState(recipient.get(), isSecureText, isDefaultSms);
|
||||||
|
onSecurityUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Initializers
|
///// Initializers
|
||||||
|
@ -1643,6 +1646,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draftText != null) {
|
if (draftText != null) {
|
||||||
|
Log.d(TAG, "Handling shared text");
|
||||||
composeText.setText("");
|
composeText.setText("");
|
||||||
composeText.append(draftText);
|
composeText.append(draftText);
|
||||||
result.set(true);
|
result.set(true);
|
||||||
|
@ -1654,6 +1658,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draftText == null && draftMedia == null && draftMediaType == null) {
|
if (draftText == null && draftMedia == null && draftMediaType == null) {
|
||||||
|
Log.d(TAG, "Initializing draft from database");
|
||||||
return initializeDraftFromDatabase();
|
return initializeDraftFromDatabase();
|
||||||
} else {
|
} else {
|
||||||
updateToggleButtonState();
|
updateToggleButtonState();
|
||||||
|
@ -1844,60 +1849,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListenableFuture<Boolean> initializeSecurity(final boolean currentSecureText,
|
|
||||||
final boolean currentIsDefaultSms)
|
|
||||||
{
|
|
||||||
final SettableFuture<Boolean> future = new SettableFuture<>();
|
|
||||||
final Context context = requireContext().getApplicationContext();
|
|
||||||
|
|
||||||
handleSecurityChange(currentSecureText || isPushGroupConversation(), currentIsDefaultSms);
|
|
||||||
|
|
||||||
new AsyncTask<Recipient, Void, boolean[]>() {
|
|
||||||
@Override
|
|
||||||
protected boolean[] doInBackground(Recipient... params) {
|
|
||||||
Recipient recipient = params[0].resolve();
|
|
||||||
Log.i(TAG, "Resolving registered state...");
|
|
||||||
RegisteredState registeredState;
|
|
||||||
|
|
||||||
if (recipient.isPushGroup()) {
|
|
||||||
Log.i(TAG, "Push group recipient...");
|
|
||||||
registeredState = RegisteredState.REGISTERED;
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Checking through resolved recipient");
|
|
||||||
registeredState = recipient.resolve().getRegistered();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Resolved registered state: " + registeredState);
|
|
||||||
boolean signalEnabled = Recipient.self().isRegistered();
|
|
||||||
|
|
||||||
if (registeredState == RegisteredState.UNKNOWN) {
|
|
||||||
try {
|
|
||||||
Log.i(TAG, "Refreshing directory for user: " + recipient.getId().serialize());
|
|
||||||
registeredState = ContactDiscovery.refresh(context, recipient, false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Returning registered state...");
|
|
||||||
return new boolean[] {registeredState == RegisteredState.REGISTERED && signalEnabled,
|
|
||||||
Util.isDefaultSmsProvider(context)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(boolean[] result) {
|
|
||||||
if (result[0] != currentSecureText || result[1] != currentIsDefaultSms) {
|
|
||||||
Log.i(TAG, "onPostExecute() handleSecurityChange: " + result[0] + " , " + result[1]);
|
|
||||||
handleSecurityChange(result[0], result[1]);
|
|
||||||
}
|
|
||||||
future.set(true);
|
|
||||||
onSecurityUpdated();
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSecurityUpdated() {
|
private void onSecurityUpdated() {
|
||||||
Log.i(TAG, "onSecurityUpdated()");
|
Log.i(TAG, "onSecurityUpdated()");
|
||||||
updateReminders();
|
updateReminders();
|
||||||
|
@ -1989,22 +1940,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
sendButton.setDefaultSubscriptionId(defaultSubscriptionId.orElse(null));
|
sendButton.setDefaultSubscriptionId(defaultSubscriptionId.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMmsEnabledCheck() {
|
|
||||||
final Context context = requireContext().getApplicationContext();
|
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Boolean>() {
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
return Util.isMmsCapable(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean isMmsEnabled) {
|
|
||||||
ConversationParentFragment.this.isMmsEnabled = isMmsEnabled;
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListenableFuture<Boolean> initializeIdentityRecords() {
|
private ListenableFuture<Boolean> initializeIdentityRecords() {
|
||||||
final SettableFuture<Boolean> future = new SettableFuture<>();
|
final SettableFuture<Boolean> future = new SettableFuture<>();
|
||||||
final Context context = requireContext().getApplicationContext();
|
final Context context = requireContext().getApplicationContext();
|
||||||
|
@ -2594,7 +2529,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
|
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
|
||||||
updatePaymentsAvailable();
|
updatePaymentsAvailable();
|
||||||
updateSendButtonColor(sendButton.getSelectedSendType());
|
updateSendButtonColor(sendButton.getSelectedSendType());
|
||||||
initializeSecurity(isSecureText, isDefaultSms);
|
|
||||||
|
|
||||||
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
|
if (searchViewItem == null || !searchViewItem.isActionViewExpanded()) {
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
|
@ -2658,7 +2592,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
securityUpdateReceiver = new BroadcastReceiver() {
|
securityUpdateReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
initializeSecurity(isSecureText, isDefaultSms);
|
viewModel.updateSecurityInfo();
|
||||||
calculateCharactersRemaining();
|
calculateCharactersRemaining();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3024,7 +2958,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
|
|
||||||
Log.i(TAG, "[sendMessage] recipient: " + recipient.getId() + ", threadId: " + threadId + ", sendType: " + (sendType.usesSignalTransport() ? "signal" : "sms") + ", isManual: " + sendButton.isManualSelection());
|
Log.i(TAG, "[sendMessage] recipient: " + recipient.getId() + ", threadId: " + threadId + ", sendType: " + (sendType.usesSignalTransport() ? "signal" : "sms") + ", isManual: " + sendButton.isManualSelection());
|
||||||
|
|
||||||
if ((recipient.isMmsGroup() || recipient.getEmail().isPresent()) && !isMmsEnabled) {
|
if ((recipient.isMmsGroup() || recipient.getEmail().isPresent()) && !viewModel.getConversationStateSnapshot().isMmsEnabled()) {
|
||||||
handleManualMmsRequired();
|
handleManualMmsRequired();
|
||||||
} else if (sendType.usesSignalTransport() && (identityRecords.isUnverified(true) || identityRecords.isUntrusted(true))) {
|
} else if (sendType.usesSignalTransport() && (identityRecords.isUnverified(true) || identityRecords.isUntrusted(true))) {
|
||||||
handleRecentSafetyNumberChange();
|
handleRecentSafetyNumberChange();
|
||||||
|
|
|
@ -8,23 +8,33 @@ import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.BubbleUtil;
|
import org.thoughtcrime.securesms.util.BubbleUtil;
|
||||||
import org.thoughtcrime.securesms.util.ConversationUtil;
|
import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
|
import io.reactivex.rxjava3.core.Scheduler;
|
||||||
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
class ConversationRepository {
|
class ConversationRepository {
|
||||||
|
|
||||||
private static final String TAG = Log.tag(ConversationRepository.class);
|
private static final String TAG = Log.tag(ConversationRepository.class);
|
||||||
|
@ -115,4 +125,41 @@ class ConversationRepository {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull Single<Boolean> checkIfMmsIsEnabled() {
|
||||||
|
return Single.fromCallable(() -> Util.isMmsCapable(context)).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull Observable<ConversationSecurityInfo> getSecurityInfo(RecipientId recipientId) {
|
||||||
|
return Recipient.observable(recipientId).map(recipient -> {
|
||||||
|
Log.i(TAG, "Resolving registered state...");
|
||||||
|
RecipientDatabase.RegisteredState registeredState;
|
||||||
|
|
||||||
|
if (recipient.isPushGroup()) {
|
||||||
|
Log.i(TAG, "Push group recipient...");
|
||||||
|
registeredState = RecipientDatabase.RegisteredState.REGISTERED;
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Checking through resolved recipient");
|
||||||
|
registeredState = recipient.resolve().getRegistered();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Resolved registered state: " + registeredState);
|
||||||
|
boolean signalEnabled = Recipient.self().isRegistered();
|
||||||
|
|
||||||
|
if (registeredState == RecipientDatabase.RegisteredState.UNKNOWN) {
|
||||||
|
try {
|
||||||
|
Log.i(TAG, "Refreshing directory for user: " + recipient.getId().serialize());
|
||||||
|
registeredState = ContactDiscovery.refresh(context, recipient, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Returning registered state...");
|
||||||
|
return new ConversationSecurityInfo(recipientId,
|
||||||
|
registeredState == RecipientDatabase.RegisteredState.REGISTERED && signalEnabled,
|
||||||
|
Util.isDefaultSmsProvider(context),
|
||||||
|
true);
|
||||||
|
}).subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.thoughtcrime.securesms.conversation
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
|
||||||
|
data class ConversationSecurityInfo(
|
||||||
|
val recipientId: RecipientId = RecipientId.UNKNOWN,
|
||||||
|
val isPushAvailable: Boolean = false,
|
||||||
|
val isDefaultSmsApplication: Boolean = false,
|
||||||
|
val isInitialized: Boolean = false
|
||||||
|
)
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.thoughtcrime.securesms.conversation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State holder for different values we are interested in for a given
|
||||||
|
* conversation. This is to be used for different values normally stored
|
||||||
|
* directly in the fragment that would be better relegated to a ViewModel.
|
||||||
|
*/
|
||||||
|
data class ConversationState(
|
||||||
|
val isMmsEnabled: Boolean = true,
|
||||||
|
val securityInfo: ConversationSecurityInfo = ConversationSecurityInfo()
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun create(): ConversationState {
|
||||||
|
return ConversationState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withMmsEnabled(isMmsEnabled: Boolean) = copy(isMmsEnabled = isMmsEnabled)
|
||||||
|
fun withSecurityInfo(securityInfo: ConversationSecurityInfo) = copy(securityInfo = securityInfo)
|
||||||
|
}
|
|
@ -17,12 +17,12 @@ import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.signal.libsignal.protocol.util.Pair;
|
||||||
import org.signal.paging.ObservablePagedData;
|
import org.signal.paging.ObservablePagedData;
|
||||||
import org.signal.paging.PagedData;
|
import org.signal.paging.PagedData;
|
||||||
import org.signal.paging.PagingConfig;
|
import org.signal.paging.PagingConfig;
|
||||||
import org.signal.paging.PagingController;
|
import org.signal.paging.PagingController;
|
||||||
import org.signal.paging.ProxyPagingController;
|
import org.signal.paging.ProxyPagingController;
|
||||||
import org.signal.libsignal.protocol.util.Pair;
|
|
||||||
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.NotificationProfilesRepository;
|
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.NotificationProfilesRepository;
|
||||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
|
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
|
||||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper;
|
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper;
|
||||||
|
@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store;
|
import org.thoughtcrime.securesms.util.livedata.Store;
|
||||||
|
import org.thoughtcrime.securesms.util.rx.RxStore;
|
||||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -55,9 +56,12 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.BackpressureStrategy;
|
import io.reactivex.rxjava3.core.BackpressureStrategy;
|
||||||
|
import io.reactivex.rxjava3.core.Flowable;
|
||||||
import io.reactivex.rxjava3.core.Observable;
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||||
|
import kotlin.Unit;
|
||||||
|
|
||||||
public class ConversationViewModel extends ViewModel {
|
public class ConversationViewModel extends ViewModel {
|
||||||
|
|
||||||
|
@ -88,6 +92,9 @@ public class ConversationViewModel extends ViewModel {
|
||||||
private final NotificationProfilesRepository notificationProfilesRepository;
|
private final NotificationProfilesRepository notificationProfilesRepository;
|
||||||
private final MutableLiveData<String> searchQuery;
|
private final MutableLiveData<String> searchQuery;
|
||||||
private final GroupAuthorNameColorHelper groupAuthorNameColorHelper;
|
private final GroupAuthorNameColorHelper groupAuthorNameColorHelper;
|
||||||
|
private final RxStore<ConversationState> conversationStateStore;
|
||||||
|
private final CompositeDisposable disposables;
|
||||||
|
private final BehaviorSubject<Unit> conversationStateTick;
|
||||||
|
|
||||||
private ConversationIntents.Args args;
|
private ConversationIntents.Args args;
|
||||||
private int jumpToPosition;
|
private int jumpToPosition;
|
||||||
|
@ -113,6 +120,9 @@ public class ConversationViewModel extends ViewModel {
|
||||||
this.recipientId = BehaviorSubject.create();
|
this.recipientId = BehaviorSubject.create();
|
||||||
this.threadId = BehaviorSubject.create();
|
this.threadId = BehaviorSubject.create();
|
||||||
this.groupAuthorNameColorHelper = new GroupAuthorNameColorHelper();
|
this.groupAuthorNameColorHelper = new GroupAuthorNameColorHelper();
|
||||||
|
this.conversationStateStore = new RxStore<>(ConversationState.create(), Schedulers.io());
|
||||||
|
this.disposables = new CompositeDisposable();
|
||||||
|
this.conversationStateTick = BehaviorSubject.createDefault(Unit.INSTANCE);
|
||||||
|
|
||||||
BehaviorSubject<Recipient> recipientCache = BehaviorSubject.create();
|
BehaviorSubject<Recipient> recipientCache = BehaviorSubject.create();
|
||||||
|
|
||||||
|
@ -122,6 +132,12 @@ public class ConversationViewModel extends ViewModel {
|
||||||
.map(Recipient::resolved)
|
.map(Recipient::resolved)
|
||||||
.subscribe(recipientCache);
|
.subscribe(recipientCache);
|
||||||
|
|
||||||
|
conversationStateStore.update(Observable.combineLatest(recipientId, conversationStateTick, (id, tick) -> id)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.switchMap(conversationRepository::getSecurityInfo)
|
||||||
|
.toFlowable(BackpressureStrategy.LATEST),
|
||||||
|
(securityInfo, state) -> state.withSecurityInfo(securityInfo));
|
||||||
|
|
||||||
BehaviorSubject<ConversationData> conversationMetadata = BehaviorSubject.create();
|
BehaviorSubject<ConversationData> conversationMetadata = BehaviorSubject.create();
|
||||||
|
|
||||||
Observable.combineLatest(threadId, recipientCache, Pair::new)
|
Observable.combineLatest(threadId, recipientCache, Pair::new)
|
||||||
|
@ -270,6 +286,29 @@ public class ConversationViewModel extends ViewModel {
|
||||||
conversationRepository.markGiftBadgeRevealed(messageId);
|
conversationRepository.markGiftBadgeRevealed(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkIfMmsIsEnabled() {
|
||||||
|
disposables.add(conversationRepository.checkIfMmsIsEnabled().subscribe(isEnabled -> {
|
||||||
|
conversationStateStore.update(state -> state.withMmsEnabled(true));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull Flowable<ConversationState> getConversationState() {
|
||||||
|
return conversationStateStore.getStateFlowable().observeOn(AndroidSchedulers.mainThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull Flowable<ConversationSecurityInfo> getConversationSecurityInfo(@NonNull RecipientId recipientId) {
|
||||||
|
return getConversationState().map(ConversationState::getSecurityInfo)
|
||||||
|
.filter(info -> info.isInitialized() && Objects.equals(info.getRecipientId(), recipientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSecurityInfo() {
|
||||||
|
conversationStateTick.onNext(Unit.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull ConversationState getConversationStateSnapshot() {
|
||||||
|
return conversationStateStore.getState();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull LiveData<String> getSearchQuery() {
|
@NonNull LiveData<String> getSearchQuery() {
|
||||||
return searchQuery;
|
return searchQuery;
|
||||||
}
|
}
|
||||||
|
@ -372,6 +411,7 @@ public class ConversationViewModel extends ViewModel {
|
||||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(conversationObserver);
|
ApplicationDependencies.getDatabaseObserver().unregisterObserver(conversationObserver);
|
||||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageUpdateObserver);
|
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageUpdateObserver);
|
||||||
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageInsertObserver);
|
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageInsertObserver);
|
||||||
|
disposables.clear();
|
||||||
EventBus.getDefault().unregister(this);
|
EventBus.getDefault().unregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ class LifecycleDisposable : DefaultLifecycleObserver {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
disposables.clear()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy(owner: LifecycleOwner) {
|
override fun onDestroy(owner: LifecycleOwner) {
|
||||||
owner.lifecycle.removeObserver(this)
|
owner.lifecycle.removeObserver(this)
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
|
|
Ładowanie…
Reference in New Issue