Remove deprecated SMS fields from recipient table.
This commit is contained in:
parent
e7972d4903
commit
e3ec53c2d0
52 changed files with 34 additions and 2044 deletions
|
@ -17,7 +17,6 @@ object MmsHelper {
|
||||||
recipient: Recipient = Recipient.UNKNOWN,
|
recipient: Recipient = Recipient.UNKNOWN,
|
||||||
body: String = "body",
|
body: String = "body",
|
||||||
sentTimeMillis: Long = System.currentTimeMillis(),
|
sentTimeMillis: Long = System.currentTimeMillis(),
|
||||||
subscriptionId: Int = -1,
|
|
||||||
expiresIn: Long = 0,
|
expiresIn: Long = 0,
|
||||||
viewOnce: Boolean = false,
|
viewOnce: Boolean = false,
|
||||||
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
||||||
|
@ -32,7 +31,6 @@ object MmsHelper {
|
||||||
recipient = recipient,
|
recipient = recipient,
|
||||||
body = body,
|
body = body,
|
||||||
timestamp = sentTimeMillis,
|
timestamp = sentTimeMillis,
|
||||||
subscriptionId = subscriptionId,
|
|
||||||
expiresIn = expiresIn,
|
expiresIn = expiresIn,
|
||||||
viewOnce = viewOnce,
|
viewOnce = viewOnce,
|
||||||
distributionType = distributionType,
|
distributionType = distributionType,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.insights.InsightsOptOut;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
|
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
||||||
|
@ -30,7 +29,6 @@ public final class AppInitialization {
|
||||||
public static void onFirstEverAppLaunch(@NonNull Context context) {
|
public static void onFirstEverAppLaunch(@NonNull Context context) {
|
||||||
Log.i(TAG, "onFirstEverAppLaunch()");
|
Log.i(TAG, "onFirstEverAppLaunch()");
|
||||||
|
|
||||||
InsightsOptOut.userRequestedOptOut(context);
|
|
||||||
TextSecurePreferences.setAppMigrationVersion(context, ApplicationMigrations.CURRENT_VERSION);
|
TextSecurePreferences.setAppMigrationVersion(context, ApplicationMigrations.CURRENT_VERSION);
|
||||||
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
|
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
|
||||||
TextSecurePreferences.setLastVersionCode(context, Util.getCanonicalVersionCode());
|
TextSecurePreferences.setLastVersionCode(context, Util.getCanonicalVersionCode());
|
||||||
|
@ -71,7 +69,6 @@ public final class AppInitialization {
|
||||||
public static void onRepairFirstEverAppLaunch(@NonNull Context context) {
|
public static void onRepairFirstEverAppLaunch(@NonNull Context context) {
|
||||||
Log.w(TAG, "onRepairFirstEverAppLaunch()");
|
Log.w(TAG, "onRepairFirstEverAppLaunch()");
|
||||||
|
|
||||||
InsightsOptOut.userRequestedOptOut(context);
|
|
||||||
TextSecurePreferences.setAppMigrationVersion(context, ApplicationMigrations.CURRENT_VERSION);
|
TextSecurePreferences.setAppMigrationVersion(context, ApplicationMigrations.CURRENT_VERSION);
|
||||||
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
|
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
|
||||||
TextSecurePreferences.setLastVersionCode(context, Util.getCanonicalVersionCode());
|
TextSecurePreferences.setLastVersionCode(context, Util.getCanonicalVersionCode());
|
||||||
|
|
|
@ -254,13 +254,8 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
|
||||||
for (SelectedContact contact : contacts) {
|
for (SelectedContact contact : contacts) {
|
||||||
RecipientId recipientId = contact.getOrCreateRecipientId(context);
|
RecipientId recipientId = contact.getOrCreateRecipientId(context);
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
int subscriptionId = recipient.getDefaultSubscriptionId().orElse(-1);
|
|
||||||
|
|
||||||
MessageSender.send(context, OutgoingMessage.sms(recipient, message, subscriptionId), -1L, MessageSender.SendType.SMS, null, null);
|
MessageSender.send(context, OutgoingMessage.sms(recipient, message), -1L, MessageSender.SendType.SMS, null, null);
|
||||||
|
|
||||||
if (recipient.getContactUri() != null) {
|
|
||||||
SignalDatabase.recipients().setHasSentInvite(recipient.getId());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import org.signal.core.util.concurrent.LifecycleDisposable;
|
||||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
|
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
||||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
|
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
|
||||||
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
|
@ -78,10 +77,6 @@ public class MainNavigator {
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void goToInsights() {
|
|
||||||
InsightsLauncher.showInsightsDashboard(activity.getSupportFragmentManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
private @NonNull FragmentManager getFragmentManager() {
|
private @NonNull FragmentManager getFragmentManager() {
|
||||||
return activity.getSupportFragmentManager();
|
return activity.getSupportFragmentManager();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.components.reminder;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
|
|
||||||
public final class FirstInviteReminder extends Reminder {
|
|
||||||
|
|
||||||
private final int percentIncrease;
|
|
||||||
|
|
||||||
public FirstInviteReminder(final int percentIncrease) {
|
|
||||||
super(R.string.FirstInviteReminder__title, NO_RESOURCE);
|
|
||||||
this.percentIncrease = percentIncrease;
|
|
||||||
|
|
||||||
addAction(new Action(R.string.InsightsReminder__invite, R.id.reminder_action_invite));
|
|
||||||
addAction(new Action(R.string.InsightsReminder__view_insights, R.id.reminder_action_view_insights));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull CharSequence getText(@NonNull Context context) {
|
|
||||||
return context.getString(R.string.FirstInviteReminder__description, percentIncrease);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.components.reminder;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
|
|
||||||
public final class SecondInviteReminder extends Reminder {
|
|
||||||
|
|
||||||
private final Recipient recipient;
|
|
||||||
private final int progress;
|
|
||||||
|
|
||||||
public SecondInviteReminder(final @NonNull Context context,
|
|
||||||
final @NonNull Recipient recipient,
|
|
||||||
final int percent)
|
|
||||||
{
|
|
||||||
super(R.string.SecondInviteReminder__title, NO_RESOURCE);
|
|
||||||
this.recipient = recipient;
|
|
||||||
|
|
||||||
this.progress = percent;
|
|
||||||
|
|
||||||
addAction(new Action(R.string.InsightsReminder__invite, R.id.reminder_action_invite));
|
|
||||||
addAction(new Action(R.string.InsightsReminder__view_insights, R.id.reminder_action_view_insights));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull CharSequence getText(@NonNull Context context) {
|
|
||||||
return context.getString(R.string.SecondInviteReminder__description, recipient.getDisplayName(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getProgress() {
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -573,7 +573,7 @@ open class ContactSearchAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSmsContact(model: T): Boolean {
|
private fun isSmsContact(model: T): Boolean {
|
||||||
return (getRecipient(model).isForceSmsSelection || getRecipient(model).isUnregistered) && !getRecipient(model).isDistributionList
|
return getRecipient(model).isUnregistered && !getRecipient(model).isDistributionList
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isNotRegistered(model: T): Boolean {
|
private fun isNotRegistered(model: T): Boolean {
|
||||||
|
|
|
@ -184,10 +184,7 @@ import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog;
|
||||||
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.ManagePendingAndRequestingMembersActivity;
|
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.ManagePendingAndRequestingMembersActivity;
|
||||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInitiationBottomSheetDialogFragment;
|
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInitiationBottomSheetDialogFragment;
|
||||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestionsDialog;
|
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestionsDialog;
|
||||||
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
|
||||||
import org.thoughtcrime.securesms.invites.InviteActions;
|
import org.thoughtcrime.securesms.invites.InviteActions;
|
||||||
import org.thoughtcrime.securesms.invites.InviteReminderModel;
|
|
||||||
import org.thoughtcrime.securesms.invites.InviteReminderRepository;
|
|
||||||
import org.thoughtcrime.securesms.jobs.ForceUpdateGroupV2Job;
|
import org.thoughtcrime.securesms.jobs.ForceUpdateGroupV2Job;
|
||||||
import org.thoughtcrime.securesms.jobs.GroupV1MigrationJob;
|
import org.thoughtcrime.securesms.jobs.GroupV1MigrationJob;
|
||||||
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
||||||
|
@ -437,7 +434,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
private ConversationSearchViewModel searchViewModel;
|
private ConversationSearchViewModel searchViewModel;
|
||||||
private ConversationStickerViewModel stickerViewModel;
|
private ConversationStickerViewModel stickerViewModel;
|
||||||
private ConversationViewModel viewModel;
|
private ConversationViewModel viewModel;
|
||||||
private InviteReminderModel inviteReminderModel;
|
|
||||||
private ConversationGroupViewModel groupViewModel;
|
private ConversationGroupViewModel groupViewModel;
|
||||||
private MentionsPickerViewModel mentionsViewModel;
|
private MentionsPickerViewModel mentionsViewModel;
|
||||||
private InlineQueryViewModel inlineQueryViewModel;
|
private InlineQueryViewModel inlineQueryViewModel;
|
||||||
|
@ -1364,8 +1360,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
if (paymentsValues.getPaymentsAvailability().isSendAllowed() &&
|
if (paymentsValues.getPaymentsAvailability().isSendAllowed() &&
|
||||||
!recipient.get().isSelf() &&
|
!recipient.get().isSelf() &&
|
||||||
!recipient.get().isGroup() &&
|
!recipient.get().isGroup() &&
|
||||||
recipient.get().isRegistered() &&
|
recipient.get().isRegistered())
|
||||||
!recipient.get().isForceSmsSelection())
|
|
||||||
{
|
{
|
||||||
attachmentKeyboardStub.get().filterAttachmentKeyboardButtons(null);
|
attachmentKeyboardStub.get().filterAttachmentKeyboardButtons(null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1425,16 +1420,11 @@ public class ConversationParentFragment extends Fragment
|
||||||
sendButton.disableTransportType(MessageSendType.TransportType.SIGNAL);
|
sendButton.disableTransportType(MessageSendType.TransportType.SIGNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recipient.get().isPushGroup() && recipient.get().isForceSmsSelection() && smsEnabled) {
|
if (isPushAvailable || isPushGroupConversation() || recipient.get().isServiceIdOnly() || recipient.get().isReleaseNotes() || !smsEnabled) {
|
||||||
|
sendButton.setDefaultTransport(MessageSendType.TransportType.SIGNAL);
|
||||||
|
} else {
|
||||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
||||||
viewModel.insertSmsExportUpdateEvent(recipient.get());
|
viewModel.insertSmsExportUpdateEvent(recipient.get());
|
||||||
} else {
|
|
||||||
if (isPushAvailable || isPushGroupConversation() || recipient.get().isServiceIdOnly() || recipient.get().isReleaseNotes() || !smsEnabled) {
|
|
||||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SIGNAL);
|
|
||||||
} else {
|
|
||||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
|
||||||
viewModel.insertSmsExportUpdateEvent(recipient.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateCharactersRemaining();
|
calculateCharactersRemaining();
|
||||||
|
@ -1710,12 +1700,9 @@ public class ConversationParentFragment extends Fragment
|
||||||
private void onSecurityUpdated() {
|
private void onSecurityUpdated() {
|
||||||
Log.i(TAG, "onSecurityUpdated()");
|
Log.i(TAG, "onSecurityUpdated()");
|
||||||
updateReminders();
|
updateReminders();
|
||||||
updateDefaultSubscriptionId(recipient.get().getDefaultSubscriptionId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeInsightObserver() {
|
private void initializeInsightObserver() {
|
||||||
inviteReminderModel = new InviteReminderModel(requireContext(), new InviteReminderRepository(requireContext()));
|
|
||||||
inviteReminderModel.loadReminder(recipient, this::updateReminders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateReminders() {
|
protected void updateReminders() {
|
||||||
|
@ -1724,7 +1711,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Reminder> inviteReminder = inviteReminderModel.getReminder();
|
|
||||||
Integer actionableRequestingMembers = groupViewModel.getActionableRequestingMembers().getValue();
|
Integer actionableRequestingMembers = groupViewModel.getActionableRequestingMembers().getValue();
|
||||||
List<RecipientId> gv1MigrationSuggestions = groupViewModel.getGroupV1MigrationSuggestions().getValue();
|
List<RecipientId> gv1MigrationSuggestions = groupViewModel.getGroupV1MigrationSuggestions().getValue();
|
||||||
|
|
||||||
|
@ -1740,11 +1726,8 @@ public class ConversationParentFragment extends Fragment
|
||||||
} else if (SignalStore.account().isRegistered() &&
|
} else if (SignalStore.account().isRegistered() &&
|
||||||
TextSecurePreferences.isShowInviteReminders(context) &&
|
TextSecurePreferences.isShowInviteReminders(context) &&
|
||||||
!viewModel.isPushAvailable() &&
|
!viewModel.isPushAvailable() &&
|
||||||
inviteReminder.isPresent() &&
|
|
||||||
!recipient.get().isGroup()) {
|
!recipient.get().isGroup()) {
|
||||||
reminderView.get().setOnActionClickListener(this::handleReminderAction);
|
reminderView.get().setOnActionClickListener(this::handleReminderAction);
|
||||||
reminderView.get().setOnDismissListener(() -> inviteReminderModel.dismissReminder());
|
|
||||||
reminderView.get().showReminder(inviteReminder.get());
|
|
||||||
} else if (actionableRequestingMembers != null && actionableRequestingMembers > 0) {
|
} else if (actionableRequestingMembers != null && actionableRequestingMembers > 0) {
|
||||||
reminderView.get().showReminder(new PendingGroupJoinRequestsReminder(actionableRequestingMembers));
|
reminderView.get().showReminder(new PendingGroupJoinRequestsReminder(actionableRequestingMembers));
|
||||||
reminderView.get().setOnActionClickListener(id -> {
|
reminderView.get().setOnActionClickListener(id -> {
|
||||||
|
@ -1785,8 +1768,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
if (reminderActionId == R.id.reminder_action_invite) {
|
if (reminderActionId == R.id.reminder_action_invite) {
|
||||||
handleInviteLink();
|
handleInviteLink();
|
||||||
reminderView.get().requestDismiss();
|
reminderView.get().requestDismiss();
|
||||||
} else if (reminderActionId == R.id.reminder_action_view_insights) {
|
|
||||||
InsightsLauncher.showInsightsDashboard(getChildFragmentManager());
|
|
||||||
} else if (reminderActionId == R.id.reminder_action_update_now) {
|
} else if (reminderActionId == R.id.reminder_action_update_now) {
|
||||||
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext());
|
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext());
|
||||||
} else if (reminderActionId == R.id.reminder_action_re_register) {
|
} else if (reminderActionId == R.id.reminder_action_re_register) {
|
||||||
|
@ -1958,8 +1939,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
composeText.setMessageSendType(newMessageSendType);
|
composeText.setMessageSendType(newMessageSendType);
|
||||||
|
|
||||||
updateSendButtonColor(newMessageSendType);
|
updateSendButtonColor(newMessageSendType);
|
||||||
|
|
||||||
if (manuallySelected) recordTransportPreference(newMessageSendType);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
titleView.setOnStoryRingClickListener(v -> handleStoryRingClick());
|
titleView.setOnStoryRingClickListener(v -> handleStoryRingClick());
|
||||||
|
@ -2461,7 +2440,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
titleView.setVerified(identityRecords.isVerified() && !recipient.isSelf());
|
titleView.setVerified(identityRecords.isVerified() && !recipient.isSelf());
|
||||||
setBlockedUserState(recipient, viewModel.getConversationStateSnapshot().getSecurityInfo());
|
setBlockedUserState(recipient, viewModel.getConversationStateSnapshot().getSecurityInfo());
|
||||||
updateReminders();
|
updateReminders();
|
||||||
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
|
|
||||||
updatePaymentsAvailable();
|
updatePaymentsAvailable();
|
||||||
updateSendButtonColor(sendButton.getSelectedSendType());
|
updateSendButtonColor(sendButton.getSelectedSendType());
|
||||||
|
|
||||||
|
@ -2904,7 +2882,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
result.getBody(),
|
result.getBody(),
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
-1,
|
|
||||||
expiresIn,
|
expiresIn,
|
||||||
result.isViewOnce(),
|
result.isViewOnce(),
|
||||||
distributionType,
|
distributionType,
|
||||||
|
@ -3033,7 +3010,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
OutgoingMessage.buildMessage(slideDeck, body),
|
OutgoingMessage.buildMessage(slideDeck, body),
|
||||||
slideDeck.asAttachments(),
|
slideDeck.asAttachments(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
sendType.getSimSubscriptionIdOr(-1),
|
|
||||||
expiresIn,
|
expiresIn,
|
||||||
viewOnce,
|
viewOnce,
|
||||||
distributionType,
|
distributionType,
|
||||||
|
@ -3126,7 +3102,7 @@ public class ConversationParentFragment extends Fragment
|
||||||
}
|
}
|
||||||
ApplicationDependencies.getTypingStatusSender().onTypingStopped(thread);
|
ApplicationDependencies.getTypingStatusSender().onTypingStopped(thread);
|
||||||
} else {
|
} else {
|
||||||
message = OutgoingMessage.sms(recipient.get(), messageBody, sendType.getSimSubscriptionIdOr(-1));
|
message = OutgoingMessage.sms(recipient.get(), messageBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
Permissions.with(this)
|
Permissions.with(this)
|
||||||
|
@ -3246,23 +3222,6 @@ public class ConversationParentFragment extends Fragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordTransportPreference(MessageSendType sendType) {
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params) {
|
|
||||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
|
||||||
|
|
||||||
recipientTable.setDefaultSubscriptionId(recipient.getId(), sendType.getSimSubscriptionIdOr(-1));
|
|
||||||
|
|
||||||
if (!recipient.resolve().isPushGroup()) {
|
|
||||||
recipientTable.setForceSmsSelection(recipient.getId(), recipient.get().getRegistered() == RegisteredState.REGISTERED && sendType.usesSmsTransport());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRecorderPermissionRequired() {
|
public void onRecorderPermissionRequired() {
|
||||||
Permissions.with(this)
|
Permissions.with(this)
|
||||||
|
|
|
@ -104,8 +104,7 @@ class AttachmentKeyboardFragment : LoggingFragment(R.layout.attachment_keyboard_
|
||||||
if (paymentsValues.paymentsAvailability.isSendAllowed &&
|
if (paymentsValues.paymentsAvailability.isSendAllowed &&
|
||||||
!recipient.isSelf &&
|
!recipient.isSelf &&
|
||||||
!recipient.isGroup &&
|
!recipient.isGroup &&
|
||||||
recipient.isRegistered &&
|
recipient.isRegistered
|
||||||
!recipient.isForceSmsSelection
|
|
||||||
) {
|
) {
|
||||||
attachmentKeyboardView.filterAttachmentKeyboardButtons(null)
|
attachmentKeyboardView.filterAttachmentKeyboardButtons(null)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -138,7 +138,6 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs;
|
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs;
|
||||||
import org.thoughtcrime.securesms.groups.SelectionLimits;
|
import org.thoughtcrime.securesms.groups.SelectionLimits;
|
||||||
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
|
||||||
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity;
|
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity;
|
||||||
|
@ -472,10 +471,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||||
itemAnimator.disable();
|
itemAnimator.disable();
|
||||||
SpoilerAnnotation.resetRevealedSpoilers();
|
SpoilerAnnotation.resetRevealedSpoilers();
|
||||||
|
|
||||||
if (Util.isDefaultSmsProvider(requireContext())) {
|
|
||||||
InsightsLauncher.showInsightsModal(requireContext(), requireFragmentManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!requireCallback().getSearchToolbar().resolved() || !(requireCallback().getSearchToolbar().get().getVisibility() == View.VISIBLE)) && list.getAdapter() != defaultAdapter) {
|
if ((!requireCallback().getSearchToolbar().resolved() || !(requireCallback().getSearchToolbar().get().getVisibility() == View.VISIBLE)) && list.getAdapter() != defaultAdapter) {
|
||||||
setAdapter(defaultAdapter);
|
setAdapter(defaultAdapter);
|
||||||
}
|
}
|
||||||
|
@ -561,7 +556,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
public void onPrepareOptionsMenu(Menu menu) {
|
||||||
menu.findItem(R.id.menu_insights).setVisible(Util.isDefaultSmsProvider(requireContext()));
|
|
||||||
menu.findItem(R.id.menu_clear_passphrase).setVisible(!TextSecurePreferences.isPasswordDisabled(requireContext()));
|
menu.findItem(R.id.menu_clear_passphrase).setVisible(!TextSecurePreferences.isPasswordDisabled(requireContext()));
|
||||||
|
|
||||||
ConversationFilterRequest request = viewModel.getConversationFilterRequest();
|
ConversationFilterRequest request = viewModel.getConversationFilterRequest();
|
||||||
|
@ -592,9 +586,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||||
} else if (itemId == R.id.menu_invite) {
|
} else if (itemId == R.id.menu_invite) {
|
||||||
handleInvite();
|
handleInvite();
|
||||||
return true;
|
return true;
|
||||||
} else if (itemId == R.id.menu_insights) {
|
|
||||||
handleInsights();
|
|
||||||
return true;
|
|
||||||
} else if (itemId == R.id.menu_notification_profile) {
|
} else if (itemId == R.id.menu_notification_profile) {
|
||||||
handleNotificationProfile();
|
handleNotificationProfile();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1143,10 +1134,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||||
getNavigator().goToInvite();
|
getNavigator().goToInvite();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInsights() {
|
|
||||||
getNavigator().goToInsights();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleNotificationProfile() {
|
private void handleNotificationProfile() {
|
||||||
NotificationProfileSelectionFragment.show(getParentFragmentManager());
|
NotificationProfileSelectionFragment.show(getParentFragmentManager());
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchove
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange
|
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange
|
||||||
import org.thoughtcrime.securesms.insights.InsightsConstants
|
|
||||||
import org.thoughtcrime.securesms.jobs.OptimizeMessageSearchIndexJob
|
import org.thoughtcrime.securesms.jobs.OptimizeMessageSearchIndexJob
|
||||||
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob
|
import org.thoughtcrime.securesms.jobs.ThreadUpdateJob
|
||||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob
|
import org.thoughtcrime.securesms.jobs.TrimThreadJob
|
||||||
|
@ -1084,10 +1083,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||||
if (unread && editedMessage == null) {
|
if (unread && editedMessage == null) {
|
||||||
threads.incrementUnread(threadId, 1, 0)
|
threads.incrementUnread(threadId, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.subscriptionId != -1) {
|
|
||||||
recipients.setDefaultSubscriptionId(message.authorId, message.subscriptionId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id
|
id
|
||||||
|
@ -2507,7 +2502,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||||
body = body,
|
body = body,
|
||||||
attachments = attachments,
|
attachments = attachments,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
subscriptionId = subscriptionId,
|
|
||||||
expiresIn = expiresIn,
|
expiresIn = expiresIn,
|
||||||
viewOnce = viewOnce,
|
viewOnce = viewOnce,
|
||||||
distributionType = distributionType,
|
distributionType = distributionType,
|
||||||
|
@ -3712,15 +3706,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||||
.readToSingleInt()
|
.readToSingleInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInsecureMessageSentCount(threadId: Long): Int {
|
|
||||||
return readableDatabase
|
|
||||||
.select("COUNT(*)")
|
|
||||||
.from(TABLE_NAME)
|
|
||||||
.where("$THREAD_ID = ? AND $outgoingInsecureMessageClause AND $DATE_SENT > ?", threadId, (System.currentTimeMillis() - InsightsConstants.PERIOD_IN_MILLIS))
|
|
||||||
.run()
|
|
||||||
.readToSingleInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSecureMessageCount(threadId: Long): Int {
|
fun getSecureMessageCount(threadId: Long): Int {
|
||||||
return readableDatabase
|
return readableDatabase
|
||||||
.select("COUNT(*)")
|
.select("COUNT(*)")
|
||||||
|
@ -3739,14 +3724,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||||
.readToSingleInt()
|
.readToSingleInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInsecureMessageCountForInsights(): Int {
|
|
||||||
return getMessageCountForRecipientsAndType(outgoingInsecureMessageClause)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSecureMessageCountForInsights(): Int {
|
|
||||||
return getMessageCountForRecipientsAndType(outgoingSecureMessageClause)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasSmsExportMessage(threadId: Long): Boolean {
|
private fun hasSmsExportMessage(threadId: Long): Boolean {
|
||||||
return readableDatabase
|
return readableDatabase
|
||||||
.exists(TABLE_NAME)
|
.exists(TABLE_NAME)
|
||||||
|
@ -3754,15 +3731,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMessageCountForRecipientsAndType(typeClause: String): Int {
|
|
||||||
return readableDatabase
|
|
||||||
.select("COUNT(*)")
|
|
||||||
.from(TABLE_NAME)
|
|
||||||
.where("$typeClause AND $DATE_SENT > ?", (System.currentTimeMillis() - InsightsConstants.PERIOD_IN_MILLIS))
|
|
||||||
.run()
|
|
||||||
.readToSingleInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val outgoingInsecureMessageClause = "($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_TYPE} AND NOT ($TYPE & ${MessageTypes.SECURE_MESSAGE_BIT})"
|
private val outgoingInsecureMessageClause = "($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_TYPE} AND NOT ($TYPE & ${MessageTypes.SECURE_MESSAGE_BIT})"
|
||||||
private val outgoingSecureMessageClause = "($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_TYPE} AND ($TYPE & ${MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT})"
|
private val outgoingSecureMessageClause = "($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_TYPE} AND ($TYPE & ${MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT})"
|
||||||
|
|
||||||
|
|
|
@ -285,8 +285,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
CALL_VIBRATE,
|
CALL_VIBRATE,
|
||||||
MUTE_UNTIL,
|
MUTE_UNTIL,
|
||||||
AVATAR_COLOR,
|
AVATAR_COLOR,
|
||||||
SEEN_INVITE_REMINDER,
|
|
||||||
DEFAULT_SUBSCRIPTION_ID,
|
|
||||||
MESSAGE_EXPIRATION_TIME,
|
MESSAGE_EXPIRATION_TIME,
|
||||||
REGISTERED,
|
REGISTERED,
|
||||||
PROFILE_KEY,
|
PROFILE_KEY,
|
||||||
|
@ -305,7 +303,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
LAST_PROFILE_FETCH,
|
LAST_PROFILE_FETCH,
|
||||||
NOTIFICATION_CHANNEL,
|
NOTIFICATION_CHANNEL,
|
||||||
UNIDENTIFIED_ACCESS_MODE,
|
UNIDENTIFIED_ACCESS_MODE,
|
||||||
FORCE_SMS_SELECTION,
|
|
||||||
CAPABILITIES,
|
CAPABILITIES,
|
||||||
STORAGE_SERVICE_ID,
|
STORAGE_SERVICE_ID,
|
||||||
MENTION_SETTING,
|
MENTION_SETTING,
|
||||||
|
@ -396,21 +393,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
private val INSIGHTS_INVITEE_LIST =
|
|
||||||
"""
|
|
||||||
SELECT $TABLE_NAME.$ID
|
|
||||||
FROM $TABLE_NAME INNER JOIN ${ThreadTable.TABLE_NAME} ON $TABLE_NAME.$ID = ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID}
|
|
||||||
WHERE
|
|
||||||
$TABLE_NAME.$GROUP_ID IS NULL AND
|
|
||||||
$TABLE_NAME.$REGISTERED = ${RegisteredState.NOT_REGISTERED.id} AND
|
|
||||||
$TABLE_NAME.$SEEN_INVITE_REMINDER < ${InsightsBannerTier.TIER_TWO.id} AND
|
|
||||||
${ThreadTable.TABLE_NAME}.${ThreadTable.HAS_SENT} AND
|
|
||||||
${ThreadTable.TABLE_NAME}.${ThreadTable.DATE} > ? AND
|
|
||||||
${ThreadTable.TABLE_NAME}.${ThreadTable.ACTIVE} = 1 AND
|
|
||||||
$TABLE_NAME.$HIDDEN = 0
|
|
||||||
ORDER BY ${ThreadTable.TABLE_NAME}.${ThreadTable.DATE} DESC LIMIT 50
|
|
||||||
"""
|
|
||||||
|
|
||||||
/** Used as a placeholder recipient for self during migrations when self isn't yet available. */
|
/** Used as a placeholder recipient for self during migrations when self isn't yet available. */
|
||||||
private val PLACEHOLDER_SELF_ID = -2L
|
private val PLACEHOLDER_SELF_ID = -2L
|
||||||
}
|
}
|
||||||
|
@ -1348,24 +1330,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDefaultSubscriptionId(id: RecipientId, defaultSubscriptionId: Int) {
|
|
||||||
val values = ContentValues().apply {
|
|
||||||
put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId)
|
|
||||||
}
|
|
||||||
if (update(id, values)) {
|
|
||||||
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setForceSmsSelection(id: RecipientId, forceSmsSelection: Boolean) {
|
|
||||||
val contentValues = ContentValues(1).apply {
|
|
||||||
put(FORCE_SMS_SELECTION, if (forceSmsSelection) 1 else 0)
|
|
||||||
}
|
|
||||||
if (update(id, contentValues)) {
|
|
||||||
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setBlocked(id: RecipientId, blocked: Boolean) {
|
fun setBlocked(id: RecipientId, blocked: Boolean) {
|
||||||
val values = ContentValues().apply {
|
val values = ContentValues().apply {
|
||||||
put(BLOCKED, if (blocked) 1 else 0)
|
put(BLOCKED, if (blocked) 1 else 0)
|
||||||
|
@ -1452,29 +1416,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
StorageSyncHelper.scheduleSyncForDataChange()
|
StorageSyncHelper.scheduleSyncForDataChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSeenFirstInviteReminder(id: RecipientId) {
|
|
||||||
setInsightsBannerTier(id, InsightsBannerTier.TIER_ONE)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSeenSecondInviteReminder(id: RecipientId) {
|
|
||||||
setInsightsBannerTier(id, InsightsBannerTier.TIER_TWO)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setHasSentInvite(id: RecipientId) {
|
|
||||||
setSeenSecondInviteReminder(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setInsightsBannerTier(id: RecipientId, insightsBannerTier: InsightsBannerTier) {
|
|
||||||
val query = "$ID = ? AND $SEEN_INVITE_REMINDER < ?"
|
|
||||||
val args = arrayOf(id.serialize(), insightsBannerTier.toString())
|
|
||||||
val values = ContentValues(1).apply {
|
|
||||||
put(SEEN_INVITE_REMINDER, insightsBannerTier.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
writableDatabase.update(TABLE_NAME, values, query, args)
|
|
||||||
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setExpireMessages(id: RecipientId, expiration: Int) {
|
fun setExpireMessages(id: RecipientId, expiration: Int) {
|
||||||
val values = ContentValues(1).apply {
|
val values = ContentValues(1).apply {
|
||||||
put(MESSAGE_EXPIRATION_TIME, expiration)
|
put(MESSAGE_EXPIRATION_TIME, expiration)
|
||||||
|
@ -3096,19 +3037,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
return operations
|
return operations
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUninvitedRecipientsForInsights(): List<RecipientId> {
|
|
||||||
val results: MutableList<RecipientId> = LinkedList()
|
|
||||||
val args = arrayOf((System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31)).toString())
|
|
||||||
|
|
||||||
readableDatabase.rawQuery(INSIGHTS_INVITEE_LIST, args).use { cursor ->
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
|
||||||
results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getRegistered(): List<RecipientId> {
|
fun getRegistered(): List<RecipientId> {
|
||||||
val results: MutableList<RecipientId> = LinkedList()
|
val results: MutableList<RecipientId> = LinkedList()
|
||||||
|
|
||||||
|
@ -3899,8 +3827,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
CHAT_COLORS to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.serialize().toByteArray() }.orElse(null),
|
CHAT_COLORS to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.serialize().toByteArray() }.orElse(null),
|
||||||
AVATAR_COLOR to primaryRecord.avatarColor.serialize(),
|
AVATAR_COLOR to primaryRecord.avatarColor.serialize(),
|
||||||
CUSTOM_CHAT_COLORS_ID to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.id.longValue }.orElse(null),
|
CUSTOM_CHAT_COLORS_ID to Optional.ofNullable(primaryRecord.chatColors).or(Optional.ofNullable(secondaryRecord.chatColors)).map { colors: ChatColors? -> colors!!.id.longValue }.orElse(null),
|
||||||
SEEN_INVITE_REMINDER to secondaryRecord.insightsBannerTier.id,
|
|
||||||
DEFAULT_SUBSCRIPTION_ID to secondaryRecord.getDefaultSubscriptionId().orElse(-1),
|
|
||||||
MESSAGE_EXPIRATION_TIME to if (primaryRecord.expireMessages > 0) primaryRecord.expireMessages else secondaryRecord.expireMessages,
|
MESSAGE_EXPIRATION_TIME to if (primaryRecord.expireMessages > 0) primaryRecord.expireMessages else secondaryRecord.expireMessages,
|
||||||
REGISTERED to RegisteredState.REGISTERED.id,
|
REGISTERED to RegisteredState.REGISTERED.id,
|
||||||
SYSTEM_GIVEN_NAME to secondaryRecord.systemProfileName.givenName,
|
SYSTEM_GIVEN_NAME to secondaryRecord.systemProfileName.givenName,
|
||||||
|
@ -4246,7 +4172,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
callVibrateState = VibrateState.fromId(cursor.requireInt(CALL_VIBRATE)),
|
callVibrateState = VibrateState.fromId(cursor.requireInt(CALL_VIBRATE)),
|
||||||
messageRingtone = Util.uri(cursor.requireString(MESSAGE_RINGTONE)),
|
messageRingtone = Util.uri(cursor.requireString(MESSAGE_RINGTONE)),
|
||||||
callRingtone = Util.uri(cursor.requireString(CALL_RINGTONE)),
|
callRingtone = Util.uri(cursor.requireString(CALL_RINGTONE)),
|
||||||
defaultSubscriptionId = cursor.requireInt(DEFAULT_SUBSCRIPTION_ID),
|
|
||||||
expireMessages = cursor.requireInt(MESSAGE_EXPIRATION_TIME),
|
expireMessages = cursor.requireInt(MESSAGE_EXPIRATION_TIME),
|
||||||
registered = RegisteredState.fromId(cursor.requireInt(REGISTERED)),
|
registered = RegisteredState.fromId(cursor.requireInt(REGISTERED)),
|
||||||
profileKey = profileKey,
|
profileKey = profileKey,
|
||||||
|
@ -4263,9 +4188,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||||
lastProfileFetch = cursor.requireLong(LAST_PROFILE_FETCH),
|
lastProfileFetch = cursor.requireLong(LAST_PROFILE_FETCH),
|
||||||
notificationChannel = cursor.requireString(NOTIFICATION_CHANNEL),
|
notificationChannel = cursor.requireString(NOTIFICATION_CHANNEL),
|
||||||
unidentifiedAccessMode = UnidentifiedAccessMode.fromMode(cursor.requireInt(UNIDENTIFIED_ACCESS_MODE)),
|
unidentifiedAccessMode = UnidentifiedAccessMode.fromMode(cursor.requireInt(UNIDENTIFIED_ACCESS_MODE)),
|
||||||
forceSmsSelection = cursor.requireBoolean(FORCE_SMS_SELECTION),
|
|
||||||
capabilities = readCapabilities(cursor),
|
capabilities = readCapabilities(cursor),
|
||||||
insightsBannerTier = InsightsBannerTier.fromId(cursor.requireInt(SEEN_INVITE_REMINDER)),
|
|
||||||
storageId = Base64.decodeNullableOrThrow(cursor.requireString(STORAGE_SERVICE_ID)),
|
storageId = Base64.decodeNullableOrThrow(cursor.requireString(STORAGE_SERVICE_ID)),
|
||||||
mentionSetting = MentionSetting.fromId(cursor.requireInt(MENTION_SETTING)),
|
mentionSetting = MentionSetting.fromId(cursor.requireInt(MENTION_SETTING)),
|
||||||
wallpaper = chatWallpaper,
|
wallpaper = chatWallpaper,
|
||||||
|
|
|
@ -8,7 +8,6 @@ import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||||
import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus
|
import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable
|
import org.thoughtcrime.securesms.database.RecipientTable
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable.InsightsBannerTier
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting
|
import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState
|
import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode
|
import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode
|
||||||
|
@ -22,7 +21,6 @@ import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId
|
import org.whispersystems.signalservice.api.push.ServiceId
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
import org.whispersystems.signalservice.api.push.ServiceId.ACI
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId.PNI
|
import org.whispersystems.signalservice.api.push.ServiceId.PNI
|
||||||
import java.util.Optional
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database model for [RecipientTable].
|
* Database model for [RecipientTable].
|
||||||
|
@ -43,7 +41,6 @@ data class RecipientRecord(
|
||||||
val callVibrateState: VibrateState,
|
val callVibrateState: VibrateState,
|
||||||
val messageRingtone: Uri?,
|
val messageRingtone: Uri?,
|
||||||
val callRingtone: Uri?,
|
val callRingtone: Uri?,
|
||||||
private val defaultSubscriptionId: Int,
|
|
||||||
val expireMessages: Int,
|
val expireMessages: Int,
|
||||||
val registered: RegisteredState,
|
val registered: RegisteredState,
|
||||||
val profileKey: ByteArray?,
|
val profileKey: ByteArray?,
|
||||||
|
@ -63,10 +60,7 @@ data class RecipientRecord(
|
||||||
val lastProfileFetch: Long,
|
val lastProfileFetch: Long,
|
||||||
val notificationChannel: String?,
|
val notificationChannel: String?,
|
||||||
val unidentifiedAccessMode: UnidentifiedAccessMode,
|
val unidentifiedAccessMode: UnidentifiedAccessMode,
|
||||||
@get:JvmName("isForceSmsSelection")
|
|
||||||
val forceSmsSelection: Boolean,
|
|
||||||
val capabilities: Capabilities,
|
val capabilities: Capabilities,
|
||||||
val insightsBannerTier: InsightsBannerTier,
|
|
||||||
val storageId: ByteArray?,
|
val storageId: ByteArray?,
|
||||||
val mentionSetting: MentionSetting,
|
val mentionSetting: MentionSetting,
|
||||||
val wallpaper: ChatWallpaper?,
|
val wallpaper: ChatWallpaper?,
|
||||||
|
@ -85,10 +79,6 @@ data class RecipientRecord(
|
||||||
val callLinkRoomId: CallLinkRoomId?
|
val callLinkRoomId: CallLinkRoomId?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getDefaultSubscriptionId(): Optional<Int> {
|
|
||||||
return if (defaultSubscriptionId != -1) Optional.of(defaultSubscriptionId) else Optional.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun e164Only(): Boolean {
|
fun e164Only(): Boolean {
|
||||||
return this.e164 != null && this.aci == null
|
return this.e164 != null && this.aci == null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.view.animation.DecelerateInterpolator;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
|
|
||||||
final class InsightsAnimatorSetFactory {
|
|
||||||
private static final int PROGRESS_ANIMATION_DURATION = 800;
|
|
||||||
private static final int DETAILS_ANIMATION_DURATION = 200;
|
|
||||||
private static final int PERCENT_SECURE_ANIMATION_DURATION = 400;
|
|
||||||
private static final int LOTTIE_ANIMATION_DURATION = 1500;
|
|
||||||
private static final int ANIMATION_START_DELAY = PROGRESS_ANIMATION_DURATION - DETAILS_ANIMATION_DURATION;
|
|
||||||
private static final float PERCENT_SECURE_MAX_SCALE = 1.3f;
|
|
||||||
|
|
||||||
private InsightsAnimatorSetFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static AnimatorSet create(int insecurePercent,
|
|
||||||
@Nullable final UpdateListener progressUpdateListener,
|
|
||||||
@Nullable final UpdateListener detailsUpdateListener,
|
|
||||||
@Nullable final UpdateListener percentSecureListener,
|
|
||||||
@Nullable final UpdateListener lottieListener)
|
|
||||||
{
|
|
||||||
final int securePercent = 100 - insecurePercent;
|
|
||||||
final AnimatorSet animatorSet = new AnimatorSet();
|
|
||||||
final ValueAnimator[] animators = Stream.of(createProgressAnimator(securePercent, progressUpdateListener),
|
|
||||||
createDetailsAnimator(detailsUpdateListener),
|
|
||||||
createPercentSecureAnimator(percentSecureListener),
|
|
||||||
createLottieAnimator(lottieListener))
|
|
||||||
.filter(a -> a != null)
|
|
||||||
.toArray(ValueAnimator[]::new);
|
|
||||||
|
|
||||||
animatorSet.setInterpolator(new DecelerateInterpolator());
|
|
||||||
animatorSet.playTogether(animators);
|
|
||||||
|
|
||||||
return animatorSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable Animator createProgressAnimator(int securePercent, @Nullable UpdateListener updateListener) {
|
|
||||||
if (updateListener == null) return null;
|
|
||||||
|
|
||||||
final ValueAnimator progressAnimator = ValueAnimator.ofFloat(0, securePercent / 100f);
|
|
||||||
|
|
||||||
progressAnimator.setDuration(PROGRESS_ANIMATION_DURATION);
|
|
||||||
progressAnimator.addUpdateListener(animation -> updateListener.onUpdate((float) animation.getAnimatedValue()));
|
|
||||||
|
|
||||||
return progressAnimator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable Animator createDetailsAnimator(@Nullable UpdateListener updateListener) {
|
|
||||||
if (updateListener == null) return null;
|
|
||||||
|
|
||||||
final ValueAnimator detailsAnimator = ValueAnimator.ofFloat(0, 1f);
|
|
||||||
|
|
||||||
detailsAnimator.setDuration(DETAILS_ANIMATION_DURATION);
|
|
||||||
detailsAnimator.setStartDelay(ANIMATION_START_DELAY);
|
|
||||||
detailsAnimator.addUpdateListener(animation -> updateListener.onUpdate((float) animation.getAnimatedValue()));
|
|
||||||
|
|
||||||
return detailsAnimator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable Animator createPercentSecureAnimator(@Nullable UpdateListener updateListener) {
|
|
||||||
if (updateListener == null) return null;
|
|
||||||
|
|
||||||
final ValueAnimator percentSecureAnimator = ValueAnimator.ofFloat(1f, PERCENT_SECURE_MAX_SCALE, 1f);
|
|
||||||
|
|
||||||
percentSecureAnimator.setStartDelay(ANIMATION_START_DELAY);
|
|
||||||
percentSecureAnimator.setDuration(PERCENT_SECURE_ANIMATION_DURATION);
|
|
||||||
percentSecureAnimator.addUpdateListener(animation -> updateListener.onUpdate((float) animation.getAnimatedValue()));
|
|
||||||
|
|
||||||
return percentSecureAnimator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nullable Animator createLottieAnimator(@Nullable UpdateListener updateListener) {
|
|
||||||
if (updateListener == null) return null;
|
|
||||||
|
|
||||||
final ValueAnimator lottieAnimator = ValueAnimator.ofFloat(0, 1f);
|
|
||||||
|
|
||||||
lottieAnimator.setStartDelay(ANIMATION_START_DELAY);
|
|
||||||
lottieAnimator.setDuration(LOTTIE_ANIMATION_DURATION);
|
|
||||||
lottieAnimator.addUpdateListener(animation -> updateListener.onUpdate((float) animation.getAnimatedValue()));
|
|
||||||
|
|
||||||
return lottieAnimator;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateListener {
|
|
||||||
void onUpdate(float value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public final class InsightsConstants {
|
|
||||||
|
|
||||||
public static final long PERIOD_IN_DAYS = 7L;
|
|
||||||
public static final long PERIOD_IN_MILLIS = TimeUnit.DAYS.toMillis(PERIOD_IN_DAYS);
|
|
||||||
|
|
||||||
private InsightsConstants() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,271 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.airbnb.lottie.LottieAnimationView;
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.NewConversationActivity;
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.components.ArcProgressBar;
|
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public final class InsightsDashboardDialogFragment extends DialogFragment {
|
|
||||||
|
|
||||||
private TextView securePercentage;
|
|
||||||
private ArcProgressBar progress;
|
|
||||||
private View progressContainer;
|
|
||||||
private TextView tagline;
|
|
||||||
private TextView encryptedMessages;
|
|
||||||
private TextView title;
|
|
||||||
private TextView description;
|
|
||||||
private RecyclerView insecureRecipients;
|
|
||||||
private TextView locallyGenerated;
|
|
||||||
private AvatarImageView avatarImageView;
|
|
||||||
private InsightsInsecureRecipientsAdapter adapter;
|
|
||||||
private LottieAnimationView lottieAnimationView;
|
|
||||||
private AnimatorSet animatorSet;
|
|
||||||
private Button startAConversation;
|
|
||||||
private Toolbar toolbar;
|
|
||||||
private InsightsDashboardViewModel viewModel;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
|
|
||||||
requireFragmentManager().beginTransaction()
|
|
||||||
.detach(this)
|
|
||||||
.attach(this)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
if (ThemeUtil.isDarkTheme(requireActivity())) {
|
|
||||||
setStyle(STYLE_NO_FRAME, R.style.TextSecure_DarkTheme);
|
|
||||||
} else {
|
|
||||||
setStyle(STYLE_NO_FRAME, R.style.TextSecure_LightTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.insights_dashboard, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
securePercentage = view.findViewById(R.id.insights_dashboard_percent_secure);
|
|
||||||
progress = view.findViewById(R.id.insights_dashboard_progress);
|
|
||||||
progressContainer = view.findViewById(R.id.insights_dashboard_percent_container);
|
|
||||||
encryptedMessages = view.findViewById(R.id.insights_dashboard_encrypted_messages);
|
|
||||||
tagline = view.findViewById(R.id.insights_dashboard_tagline);
|
|
||||||
title = view.findViewById(R.id.insights_dashboard_make_signal_secure);
|
|
||||||
description = view.findViewById(R.id.insights_dashboard_invite_your_contacts);
|
|
||||||
insecureRecipients = view.findViewById(R.id.insights_dashboard_recycler);
|
|
||||||
locallyGenerated = view.findViewById(R.id.insights_dashboard_this_stat_was_generated_locally);
|
|
||||||
avatarImageView = view.findViewById(R.id.insights_dashboard_avatar);
|
|
||||||
startAConversation = view.findViewById(R.id.insights_dashboard_start_a_conversation);
|
|
||||||
lottieAnimationView = view.findViewById(R.id.insights_dashboard_lottie_animation);
|
|
||||||
toolbar = view.findViewById(R.id.insights_dashboard_toolbar);
|
|
||||||
|
|
||||||
setupStartAConversation();
|
|
||||||
setDashboardDetailsAlpha(0f);
|
|
||||||
setNotEnoughDataAlpha(0f);
|
|
||||||
setupToolbar();
|
|
||||||
setupRecycler();
|
|
||||||
initializeViewModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupStartAConversation() {
|
|
||||||
startAConversation.setOnClickListener(v -> startActivity(new Intent(requireActivity(), NewConversationActivity.class)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDashboardDetailsAlpha(float alpha) {
|
|
||||||
tagline.setAlpha(alpha);
|
|
||||||
title.setAlpha(alpha);
|
|
||||||
description.setAlpha(alpha);
|
|
||||||
insecureRecipients.setAlpha(alpha);
|
|
||||||
locallyGenerated.setAlpha(alpha);
|
|
||||||
encryptedMessages.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupToolbar() {
|
|
||||||
toolbar.setNavigationOnClickListener(v -> dismiss());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupRecycler() {
|
|
||||||
adapter = new InsightsInsecureRecipientsAdapter(this::handleInviteRecipient);
|
|
||||||
insecureRecipients.setAdapter(adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeViewModel() {
|
|
||||||
final InsightsDashboardViewModel.Repository repository = new InsightsRepository(requireContext());
|
|
||||||
final InsightsDashboardViewModel.Factory factory = new InsightsDashboardViewModel.Factory(repository);
|
|
||||||
|
|
||||||
viewModel = new ViewModelProvider(this, factory).get(InsightsDashboardViewModel.class);
|
|
||||||
|
|
||||||
viewModel.getState().observe(getViewLifecycleOwner(), state -> {
|
|
||||||
updateInsecurePercent(state.getData());
|
|
||||||
updateInsecureRecipients(state.getInsecureRecipients());
|
|
||||||
updateUserAvatar(state.getUserAvatar());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInsecurePercent(@Nullable InsightsData insightsData) {
|
|
||||||
if (insightsData == null) return;
|
|
||||||
|
|
||||||
if (insightsData.hasEnoughData()) {
|
|
||||||
setTitleAndDescriptionText(insightsData.getPercentInsecure());
|
|
||||||
animateProgress(insightsData.getPercentInsecure());
|
|
||||||
} else {
|
|
||||||
setNotEnoughDataText();
|
|
||||||
animateNotEnoughData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateProgress(int insecurePercent) {
|
|
||||||
startAConversation.setVisibility(View.GONE);
|
|
||||||
if (animatorSet == null) {
|
|
||||||
animatorSet = InsightsAnimatorSetFactory.create(insecurePercent,
|
|
||||||
this::setProgressPercentage,
|
|
||||||
this::setDashboardDetailsAlpha,
|
|
||||||
this::setPercentSecureScale,
|
|
||||||
insecurePercent == 0 ? this::setLottieProgress : null);
|
|
||||||
|
|
||||||
if (insecurePercent == 0) {
|
|
||||||
animatorSet.addListener(new ToolbarBackgroundColorAnimationListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
animatorSet.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setProgressPercentage(float percent) {
|
|
||||||
securePercentage.setText(String.valueOf(Math.round(percent * 100)));
|
|
||||||
progress.setProgress(percent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPercentSecureScale(float scale) {
|
|
||||||
progressContainer.setScaleX(scale);
|
|
||||||
progressContainer.setScaleY(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLottieProgress(float progress) {
|
|
||||||
lottieAnimationView.setProgress(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTitleAndDescriptionText(int insecurePercent) {
|
|
||||||
startAConversation.setVisibility(View.GONE);
|
|
||||||
progressContainer.setVisibility(View.VISIBLE);
|
|
||||||
insecureRecipients.setVisibility(View.VISIBLE);
|
|
||||||
encryptedMessages.setText(R.string.InsightsDashboardFragment__encrypted_messages);
|
|
||||||
tagline.setText(getString(R.string.InsightsDashboardFragment__signal_protocol_automatically_protected, 100 - insecurePercent, InsightsConstants.PERIOD_IN_DAYS));
|
|
||||||
|
|
||||||
if (insecurePercent == 0) {
|
|
||||||
lottieAnimationView.setVisibility(View.VISIBLE);
|
|
||||||
title.setVisibility(View.GONE);
|
|
||||||
description.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
lottieAnimationView.setVisibility(View.GONE);
|
|
||||||
title.setText(R.string.InsightsDashboardFragment__spread_the_word);
|
|
||||||
description.setText(R.string.InsightsDashboardFragment__invite_your_contacts);
|
|
||||||
title.setVisibility(View.VISIBLE);
|
|
||||||
description.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNotEnoughDataText() {
|
|
||||||
startAConversation.setVisibility(View.VISIBLE);
|
|
||||||
progressContainer.setVisibility(View.INVISIBLE);
|
|
||||||
insecureRecipients.setVisibility(View.GONE);
|
|
||||||
encryptedMessages.setText(R.string.InsightsDashboardFragment__not_enough_data);
|
|
||||||
tagline.setText(getString(R.string.InsightsDashboardFragment__your_insights_percentage_is_calculated_based_on, InsightsConstants.PERIOD_IN_DAYS));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateNotEnoughData() {
|
|
||||||
if (animatorSet == null) {
|
|
||||||
animatorSet = InsightsAnimatorSetFactory.create(0, null, this::setNotEnoughDataAlpha, null, null);
|
|
||||||
animatorSet.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNotEnoughDataAlpha(float alpha) {
|
|
||||||
encryptedMessages.setAlpha(alpha);
|
|
||||||
tagline.setAlpha(alpha);
|
|
||||||
startAConversation.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInsecureRecipients(@NonNull List<Recipient> recipients) {
|
|
||||||
adapter.updateData(recipients);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUserAvatar(@Nullable InsightsUserAvatar userAvatar) {
|
|
||||||
if (userAvatar == null) avatarImageView.setImageDrawable(null);
|
|
||||||
else userAvatar.load(avatarImageView);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleInviteRecipient(final @NonNull Recipient recipient) {
|
|
||||||
new MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(getResources().getQuantityString(R.plurals.InviteActivity_send_sms_invites, 1, 1))
|
|
||||||
.setMessage(getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)))
|
|
||||||
.setPositiveButton(R.string.InsightsDashboardFragment__send, (dialog, which) -> viewModel.sendSmsInvite(recipient))
|
|
||||||
.setNegativeButton(R.string.InsightsDashboardFragment__cancel, (dialog, which) -> dialog.dismiss())
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
if (animatorSet != null) {
|
|
||||||
animatorSet.cancel();
|
|
||||||
animatorSet = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class ToolbarBackgroundColorAnimationListener implements Animator.AnimatorListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animator animation) {
|
|
||||||
toolbar.setBackgroundResource(R.color.transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
toolbar.setBackgroundColor(ThemeUtil.getThemedColor(requireContext(), android.R.attr.windowBackground));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationCancel(Animator animation) {
|
|
||||||
toolbar.setBackgroundColor(ThemeUtil.getThemedColor(requireContext(), android.R.attr.windowBackground));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationRepeat(Animator animation) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
final class InsightsDashboardState {
|
|
||||||
|
|
||||||
private final List<Recipient> insecureRecipients;
|
|
||||||
private final InsightsData insightsData;
|
|
||||||
private final InsightsUserAvatar userAvatar;
|
|
||||||
|
|
||||||
private InsightsDashboardState(@NonNull Builder builder) {
|
|
||||||
this.insecureRecipients = builder.insecureRecipients;
|
|
||||||
this.insightsData = builder.insightsData;
|
|
||||||
this.userAvatar = builder.userAvatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
static @NonNull InsightsDashboardState.Builder builder() {
|
|
||||||
return new InsightsDashboardState.Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull InsightsDashboardState.Builder buildUpon() {
|
|
||||||
return builder().withData(insightsData).withUserAvatar(userAvatar).withInsecureRecipients(insecureRecipients);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull List<Recipient> getInsecureRecipients() {
|
|
||||||
return insecureRecipients;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable InsightsUserAvatar getUserAvatar() {
|
|
||||||
return userAvatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable InsightsData getData() {
|
|
||||||
return insightsData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class Builder {
|
|
||||||
private List<Recipient> insecureRecipients = Collections.emptyList();
|
|
||||||
private InsightsUserAvatar userAvatar;
|
|
||||||
private InsightsData insightsData;
|
|
||||||
|
|
||||||
private Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull Builder withInsecureRecipients(@NonNull List<Recipient> insecureRecipients) {
|
|
||||||
this.insecureRecipients = insecureRecipients;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull Builder withData(@NonNull InsightsData insightsData) {
|
|
||||||
this.insightsData = insightsData;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull Builder withUserAvatar(@NonNull InsightsUserAvatar userAvatar) {
|
|
||||||
this.userAvatar = userAvatar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull InsightsDashboardState build() {
|
|
||||||
return new InsightsDashboardState(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.core.util.Consumer;
|
|
||||||
import androidx.lifecycle.LiveData;
|
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
import androidx.lifecycle.ViewModel;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
final class InsightsDashboardViewModel extends ViewModel {
|
|
||||||
|
|
||||||
private final MutableLiveData<InsightsDashboardState> internalState = new MutableLiveData<>(InsightsDashboardState.builder().build());
|
|
||||||
private final Repository repository;
|
|
||||||
|
|
||||||
private InsightsDashboardViewModel(@NonNull Repository repository) {
|
|
||||||
this.repository = repository;
|
|
||||||
|
|
||||||
repository.getInsightsData(data -> internalState.setValue(getNewState(b -> b.withData(data))));
|
|
||||||
repository.getUserAvatar(avatar -> internalState.setValue(getNewState(b -> b.withUserAvatar(avatar))));
|
|
||||||
updateInsecureRecipients();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInsecureRecipients() {
|
|
||||||
repository.getInsecureRecipients(recipients -> internalState.setValue(getNewState(b -> b.withInsecureRecipients(recipients))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private InsightsDashboardState getNewState(Consumer<InsightsDashboardState.Builder> builderConsumer) {
|
|
||||||
InsightsDashboardState.Builder builder = internalState.getValue().buildUpon();
|
|
||||||
builderConsumer.accept(builder);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull LiveData<InsightsDashboardState> getState() {
|
|
||||||
return internalState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendSmsInvite(@NonNull Recipient recipient) {
|
|
||||||
repository.sendSmsInvite(recipient, this::updateInsecureRecipients);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Repository {
|
|
||||||
void getInsightsData(@NonNull Consumer<InsightsData> insightsDataConsumer);
|
|
||||||
void getInsecureRecipients(@NonNull Consumer<List<Recipient>> insecureRecipientsConsumer);
|
|
||||||
void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> userAvatarConsumer);
|
|
||||||
void sendSmsInvite(@NonNull Recipient recipient, Runnable onSmsMessageSent);
|
|
||||||
}
|
|
||||||
|
|
||||||
final static class Factory implements ViewModelProvider.Factory {
|
|
||||||
|
|
||||||
private final Repository repository;
|
|
||||||
|
|
||||||
Factory(@NonNull Repository repository) {
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
|
||||||
return (T) new InsightsDashboardViewModel(repository);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
final class InsightsData {
|
|
||||||
private final boolean hasEnoughData;
|
|
||||||
private final int percentInsecure;
|
|
||||||
|
|
||||||
InsightsData(boolean hasEnoughData, int percentInsecure) {
|
|
||||||
this.hasEnoughData = hasEnoughData;
|
|
||||||
this.percentInsecure = percentInsecure;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasEnoughData() {
|
|
||||||
return hasEnoughData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPercentInsecure() {
|
|
||||||
return percentInsecure;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.core.util.Consumer;
|
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
final class InsightsInsecureRecipientsAdapter extends RecyclerView.Adapter<InsightsInsecureRecipientsAdapter.ViewHolder> {
|
|
||||||
|
|
||||||
private List<Recipient> data = Collections.emptyList();
|
|
||||||
|
|
||||||
private final Consumer<Recipient> onInviteClickedConsumer;
|
|
||||||
|
|
||||||
InsightsInsecureRecipientsAdapter(Consumer<Recipient> onInviteClickedConsumer) {
|
|
||||||
this.onInviteClickedConsumer = onInviteClickedConsumer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateData(List<Recipient> recipients) {
|
|
||||||
List<Recipient> oldData = data;
|
|
||||||
data = recipients;
|
|
||||||
|
|
||||||
DiffUtil.calculateDiff(new DiffCallback(oldData, data)).dispatchUpdatesTo(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
||||||
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.insights_dashboard_adapter_item, parent, false), this::handleInviteClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleInviteClicked(@NonNull Integer position) {
|
|
||||||
onInviteClickedConsumer.accept(data.get(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
|
||||||
holder.bind(data.get(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
|
|
||||||
private AvatarImageView avatarImageView;
|
|
||||||
private TextView displayName;
|
|
||||||
|
|
||||||
private ViewHolder(@NonNull View itemView, Consumer<Integer> onInviteClicked) {
|
|
||||||
super(itemView);
|
|
||||||
|
|
||||||
avatarImageView = itemView.findViewById(R.id.recipient_avatar);
|
|
||||||
displayName = itemView.findViewById(R.id.recipient_display_name);
|
|
||||||
|
|
||||||
Button invite = itemView.findViewById(R.id.recipient_invite);
|
|
||||||
invite.setOnClickListener(v -> {
|
|
||||||
int adapterPosition = getAdapterPosition();
|
|
||||||
|
|
||||||
if (adapterPosition == RecyclerView.NO_POSITION) return;
|
|
||||||
|
|
||||||
onInviteClicked.accept(adapterPosition);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(@NonNull Recipient recipient) {
|
|
||||||
displayName.setText(recipient.getDisplayName(itemView.getContext()));
|
|
||||||
avatarImageView.setAvatar(GlideApp.with(itemView), recipient, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DiffCallback extends DiffUtil.Callback {
|
|
||||||
|
|
||||||
private final List<Recipient> oldData;
|
|
||||||
private final List<Recipient> newData;
|
|
||||||
|
|
||||||
private DiffCallback(@NonNull List<Recipient> oldData,
|
|
||||||
@NonNull List<Recipient> newData)
|
|
||||||
{
|
|
||||||
this.oldData = oldData;
|
|
||||||
this.newData = newData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOldListSize() {
|
|
||||||
return oldData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNewListSize() {
|
|
||||||
return newData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
|
||||||
return oldData.get(oldItemPosition).getId() == newData.get(newItemPosition).getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
|
||||||
return oldData.get(oldItemPosition).equals(newData.get(newItemPosition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
|
||||||
|
|
||||||
public final class InsightsLauncher {
|
|
||||||
|
|
||||||
private static final String MODAL_TAG = "modal.fragment";
|
|
||||||
|
|
||||||
public static void showInsightsModal(@NonNull Context context, @NonNull FragmentManager fragmentManager) {
|
|
||||||
if (InsightsOptOut.userHasOptedOut(context)) return;
|
|
||||||
|
|
||||||
final Fragment fragment = fragmentManager.findFragmentByTag(MODAL_TAG);
|
|
||||||
|
|
||||||
if (fragment == null) new InsightsModalDialogFragment().show(fragmentManager, MODAL_TAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showInsightsDashboard(@NonNull FragmentManager fragmentManager) {
|
|
||||||
new InsightsDashboardDialogFragment().show(fragmentManager, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.components.ArcProgressBar;
|
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
|
||||||
|
|
||||||
public final class InsightsModalDialogFragment extends DialogFragment {
|
|
||||||
|
|
||||||
private ArcProgressBar progress;
|
|
||||||
private TextView securePercentage;
|
|
||||||
private AvatarImageView avatarImageView;
|
|
||||||
private AnimatorSet animatorSet;
|
|
||||||
private View progressContainer;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
|
|
||||||
requireFragmentManager().beginTransaction()
|
|
||||||
.detach(this)
|
|
||||||
.attach(this)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setStyle(STYLE_NO_FRAME, R.style.Theme_Signal_Insights_Modal);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
|
||||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
|
||||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.insights_modal, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
View close = view.findViewById(R.id.insights_modal_close);
|
|
||||||
Button viewInsights = view.findViewById(R.id.insights_modal_view_insights);
|
|
||||||
|
|
||||||
progress = view.findViewById(R.id.insights_modal_progress);
|
|
||||||
securePercentage = view.findViewById(R.id.insights_modal_percent_secure);
|
|
||||||
avatarImageView = view.findViewById(R.id.insights_modal_avatar);
|
|
||||||
progressContainer = view.findViewById(R.id.insights_modal_percent_container);
|
|
||||||
|
|
||||||
close.setOnClickListener(v -> dismiss());
|
|
||||||
viewInsights.setOnClickListener(v -> openInsightsAndDismiss());
|
|
||||||
|
|
||||||
initializeViewModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeViewModel() {
|
|
||||||
final InsightsModalViewModel.Repository repository = new InsightsRepository(requireContext());
|
|
||||||
final InsightsModalViewModel.Factory factory = new InsightsModalViewModel.Factory(repository);
|
|
||||||
final InsightsModalViewModel viewModel = new ViewModelProvider(this, factory).get(InsightsModalViewModel.class);
|
|
||||||
|
|
||||||
viewModel.getState().observe(getViewLifecycleOwner(), state -> {
|
|
||||||
updateInsecurePercent(state.getData());
|
|
||||||
updateUserAvatar(state.getUserAvatar());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInsecurePercent(@Nullable InsightsData insightsData) {
|
|
||||||
if (insightsData == null) return;
|
|
||||||
|
|
||||||
if (animatorSet == null) {
|
|
||||||
animatorSet = InsightsAnimatorSetFactory.create(insightsData.getPercentInsecure(), this::setProgressPercentage, null, this::setPercentSecureScale, null);
|
|
||||||
animatorSet.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setProgressPercentage(float percent) {
|
|
||||||
securePercentage.setText(String.valueOf(Math.round(percent * 100)));
|
|
||||||
progress.setProgress(percent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPercentSecureScale(float scale) {
|
|
||||||
progressContainer.setScaleX(scale);
|
|
||||||
progressContainer.setScaleY(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUserAvatar(@Nullable InsightsUserAvatar userAvatar) {
|
|
||||||
if (userAvatar == null) avatarImageView.setImageDrawable(null);
|
|
||||||
else userAvatar.load(avatarImageView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDismiss(@NonNull DialogInterface dialog) {
|
|
||||||
super.onDismiss(dialog);
|
|
||||||
InsightsOptOut.userRequestedOptOut(requireContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openInsightsAndDismiss() {
|
|
||||||
InsightsLauncher.showInsightsDashboard(requireFragmentManager());
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
if (animatorSet != null) {
|
|
||||||
animatorSet.cancel();
|
|
||||||
animatorSet = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
final class InsightsModalState {
|
|
||||||
|
|
||||||
private final InsightsData insightsData;
|
|
||||||
private final InsightsUserAvatar userAvatar;
|
|
||||||
|
|
||||||
private InsightsModalState(@NonNull Builder builder) {
|
|
||||||
this.insightsData = builder.insightsData;
|
|
||||||
this.userAvatar = builder.userAvatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
static @NonNull InsightsModalState.Builder builder() {
|
|
||||||
return new InsightsModalState.Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull InsightsModalState.Builder buildUpon() {
|
|
||||||
return builder().withUserAvatar(userAvatar).withData(insightsData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable InsightsUserAvatar getUserAvatar() {
|
|
||||||
return userAvatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable InsightsData getData() {
|
|
||||||
return insightsData;
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class Builder {
|
|
||||||
private InsightsData insightsData;
|
|
||||||
private InsightsUserAvatar userAvatar;
|
|
||||||
|
|
||||||
private Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull Builder withData(@NonNull InsightsData insightsData) {
|
|
||||||
this.insightsData = insightsData;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull Builder withUserAvatar(@NonNull InsightsUserAvatar userAvatar) {
|
|
||||||
this.userAvatar = userAvatar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull InsightsModalState build() {
|
|
||||||
return new InsightsModalState(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.core.util.Consumer;
|
|
||||||
import androidx.lifecycle.LiveData;
|
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
import androidx.lifecycle.ViewModel;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
|
|
||||||
final class InsightsModalViewModel extends ViewModel {
|
|
||||||
|
|
||||||
private final MutableLiveData<InsightsModalState> internalState = new MutableLiveData<>(InsightsModalState.builder().build());
|
|
||||||
|
|
||||||
private InsightsModalViewModel(@NonNull Repository repository) {
|
|
||||||
repository.getInsightsData(data -> internalState.setValue(getNewState(b -> b.withData(data))));
|
|
||||||
repository.getUserAvatar(avatar -> internalState.setValue(getNewState(b -> b.withUserAvatar(avatar))));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private InsightsModalState getNewState(Consumer<InsightsModalState.Builder> builderConsumer) {
|
|
||||||
InsightsModalState.Builder builder = internalState.getValue().buildUpon();
|
|
||||||
builderConsumer.accept(builder);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull LiveData<InsightsModalState> getState() {
|
|
||||||
return internalState;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Repository {
|
|
||||||
void getInsightsData(Consumer<InsightsData> insecurePercentConsumer);
|
|
||||||
void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> userAvatarConsumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
final static class Factory implements ViewModelProvider.Factory {
|
|
||||||
|
|
||||||
private final Repository repository;
|
|
||||||
|
|
||||||
Factory(@NonNull Repository repository) {
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
|
||||||
return (T) new InsightsModalViewModel(repository);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
|
|
||||||
public final class InsightsOptOut {
|
|
||||||
private static final String INSIGHTS_OPT_OUT_PREFERENCE = "insights.opt.out";
|
|
||||||
|
|
||||||
private InsightsOptOut() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean userHasOptedOut(@NonNull Context context) {
|
|
||||||
return TextSecurePreferences.getBooleanPreference(context, INSIGHTS_OPT_OUT_PREFERENCE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void userRequestedOptOut(@NonNull Context context) {
|
|
||||||
TextSecurePreferences.setBooleanPreference(context, INSIGHTS_OPT_OUT_PREFERENCE, true);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.core.util.Consumer;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
|
||||||
import org.thoughtcrime.securesms.database.MessageTable;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMessage;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
import org.signal.core.util.concurrent.SimpleTask;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class InsightsRepository implements InsightsDashboardViewModel.Repository, InsightsModalViewModel.Repository {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public InsightsRepository(Context context) {
|
|
||||||
this.context = context.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getInsightsData(@NonNull Consumer<InsightsData> insightsDataConsumer) {
|
|
||||||
SimpleTask.run(() -> {
|
|
||||||
MessageTable messageTable = SignalDatabase.messages();
|
|
||||||
int insecure = messageTable.getInsecureMessageCountForInsights();
|
|
||||||
int secure = messageTable.getSecureMessageCountForInsights();
|
|
||||||
|
|
||||||
if (insecure + secure == 0) {
|
|
||||||
return new InsightsData(false, 0);
|
|
||||||
} else {
|
|
||||||
return new InsightsData(true, Util.clamp((int) Math.ceil((insecure * 100f) / (insecure + secure)), 0, 100));
|
|
||||||
}
|
|
||||||
}, insightsDataConsumer::accept);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getInsecureRecipients(@NonNull Consumer<List<Recipient>> insecureRecipientsConsumer) {
|
|
||||||
SimpleTask.run(() -> {
|
|
||||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
|
||||||
List<RecipientId> unregisteredRecipients = recipientTable.getUninvitedRecipientsForInsights();
|
|
||||||
|
|
||||||
return Stream.of(unregisteredRecipients)
|
|
||||||
.map(Recipient::resolved)
|
|
||||||
.toList();
|
|
||||||
},
|
|
||||||
insecureRecipientsConsumer::accept);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getUserAvatar(@NonNull Consumer<InsightsUserAvatar> avatarConsumer) {
|
|
||||||
SimpleTask.run(() -> {
|
|
||||||
Recipient self = Recipient.self().resolve();
|
|
||||||
String name = Optional.of(self.getDisplayName(context)).orElse("");
|
|
||||||
|
|
||||||
return new InsightsUserAvatar(new ProfileContactPhoto(self),
|
|
||||||
self.getAvatarColor(),
|
|
||||||
new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40));
|
|
||||||
}, avatarConsumer::accept);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendSmsInvite(@NonNull Recipient recipient, Runnable onSmsMessageSent) {
|
|
||||||
SimpleTask.run(() -> {
|
|
||||||
Recipient resolved = recipient.resolve();
|
|
||||||
int subscriptionId = resolved.getDefaultSubscriptionId().orElse(-1);
|
|
||||||
String message = context.getString(R.string.InviteActivity_lets_switch_to_signal, context.getString(R.string.install_url));
|
|
||||||
|
|
||||||
MessageSender.send(context, OutgoingMessage.sms(resolved, message, subscriptionId), -1L, MessageSender.SendType.SMS, null, null);
|
|
||||||
|
|
||||||
RecipientTable database = SignalDatabase.recipients();
|
|
||||||
database.setHasSentInvite(recipient.getId());
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}, v -> onSmsMessageSent.run());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.insights;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
|
||||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
|
||||||
|
|
||||||
class InsightsUserAvatar {
|
|
||||||
private final ProfileContactPhoto profileContactPhoto;
|
|
||||||
private final AvatarColor fallbackColor;
|
|
||||||
private final FallbackContactPhoto fallbackContactPhoto;
|
|
||||||
|
|
||||||
InsightsUserAvatar(@NonNull ProfileContactPhoto profileContactPhoto, @NonNull AvatarColor fallbackColor, @NonNull FallbackContactPhoto fallbackContactPhoto) {
|
|
||||||
this.profileContactPhoto = profileContactPhoto;
|
|
||||||
this.fallbackColor = fallbackColor;
|
|
||||||
this.fallbackContactPhoto = fallbackContactPhoto;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Drawable fallbackDrawable(@NonNull Context context) {
|
|
||||||
return fallbackContactPhoto.asDrawable(context, fallbackColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void load(ImageView into) {
|
|
||||||
GlideApp.with(into)
|
|
||||||
.load(profileContactPhoto)
|
|
||||||
.error(fallbackDrawable(into.getContext()))
|
|
||||||
.circleCrop()
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
|
||||||
.into(into);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.invites;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.reminder.FirstInviteReminder;
|
|
||||||
import org.thoughtcrime.securesms.components.reminder.Reminder;
|
|
||||||
import org.thoughtcrime.securesms.components.reminder.SecondInviteReminder;
|
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.ThreadTable;
|
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.signal.core.util.concurrent.SimpleTask;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public final class InviteReminderModel {
|
|
||||||
|
|
||||||
private static final int FIRST_INVITE_REMINDER_MESSAGE_THRESHOLD = 10;
|
|
||||||
private static final int SECOND_INVITE_REMINDER_MESSAGE_THRESHOLD = 500;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Repository repository;
|
|
||||||
private final AtomicReference<ReminderInfo> reminderInfo = new AtomicReference<>();
|
|
||||||
|
|
||||||
public InviteReminderModel(@NonNull Context context, @NonNull Repository repository) {
|
|
||||||
this.context = context;
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainThread
|
|
||||||
public void loadReminder(LiveRecipient liveRecipient, Runnable reminderCheckComplete) {
|
|
||||||
SimpleTask.run(() -> createReminderInfo(liveRecipient.resolve()), result -> {
|
|
||||||
reminderInfo.set(result);
|
|
||||||
reminderCheckComplete.run();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
private @NonNull ReminderInfo createReminderInfo(Recipient recipient) {
|
|
||||||
Recipient resolved = recipient.resolve();
|
|
||||||
|
|
||||||
if (resolved.isRegistered() || resolved.isGroup() || resolved.hasSeenSecondInviteReminder()) {
|
|
||||||
return new NoReminderInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadTable threadTable = SignalDatabase.threads();
|
|
||||||
Long threadId = threadTable.getThreadIdFor(recipient.getId());
|
|
||||||
|
|
||||||
if (threadId != null) {
|
|
||||||
int conversationCount = SignalDatabase.messages().getInsecureMessageSentCount(threadId);
|
|
||||||
|
|
||||||
if (conversationCount >= SECOND_INVITE_REMINDER_MESSAGE_THRESHOLD && !resolved.hasSeenSecondInviteReminder()) {
|
|
||||||
return new SecondInviteReminderInfo(context, resolved, repository, repository.getPercentOfInsecureMessages(conversationCount));
|
|
||||||
} else if (conversationCount >= FIRST_INVITE_REMINDER_MESSAGE_THRESHOLD && !resolved.hasSeenFirstInviteReminder()) {
|
|
||||||
return new FirstInviteReminderInfo(resolved, repository, repository.getPercentOfInsecureMessages(conversationCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new NoReminderInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull Optional<Reminder> getReminder() {
|
|
||||||
ReminderInfo info = reminderInfo.get();
|
|
||||||
if (info == null) return Optional.empty();
|
|
||||||
else return Optional.ofNullable(info.reminder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dismissReminder() {
|
|
||||||
final ReminderInfo info = reminderInfo.getAndSet(null);
|
|
||||||
|
|
||||||
SimpleTask.run(() -> {
|
|
||||||
info.dismiss();
|
|
||||||
return null;
|
|
||||||
}, (v) -> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Repository {
|
|
||||||
void setHasSeenFirstInviteReminder(Recipient recipient);
|
|
||||||
void setHasSeenSecondInviteReminder(Recipient recipient);
|
|
||||||
int getPercentOfInsecureMessages(int insecureCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static abstract class ReminderInfo {
|
|
||||||
|
|
||||||
private final Reminder reminder;
|
|
||||||
|
|
||||||
ReminderInfo(Reminder reminder) {
|
|
||||||
this.reminder = reminder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
|
||||||
void dismiss() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NoReminderInfo extends ReminderInfo {
|
|
||||||
private NoReminderInfo() {
|
|
||||||
super(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FirstInviteReminderInfo extends ReminderInfo {
|
|
||||||
|
|
||||||
private final Repository repository;
|
|
||||||
private final Recipient recipient;
|
|
||||||
|
|
||||||
private FirstInviteReminderInfo(@NonNull Recipient recipient, @NonNull Repository repository, int percentInsecure) {
|
|
||||||
super(new FirstInviteReminder(percentInsecure));
|
|
||||||
|
|
||||||
this.recipient = recipient;
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WorkerThread
|
|
||||||
void dismiss() {
|
|
||||||
repository.setHasSeenFirstInviteReminder(recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SecondInviteReminderInfo extends ReminderInfo {
|
|
||||||
|
|
||||||
private final Repository repository;
|
|
||||||
private final Recipient recipient;
|
|
||||||
|
|
||||||
private SecondInviteReminderInfo(@NonNull Context context, @NonNull Recipient recipient, @NonNull Repository repository, int percentInsecure) {
|
|
||||||
super(new SecondInviteReminder(context, recipient, percentInsecure));
|
|
||||||
|
|
||||||
this.repository = repository;
|
|
||||||
this.recipient = recipient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WorkerThread
|
|
||||||
void dismiss() {
|
|
||||||
repository.setHasSeenSecondInviteReminder(recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.invites;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.MessageTable;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
|
|
||||||
public final class InviteReminderRepository implements InviteReminderModel.Repository {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public InviteReminderRepository(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHasSeenFirstInviteReminder(Recipient recipient) {
|
|
||||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
|
||||||
recipientTable.setSeenFirstInviteReminder(recipient.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHasSeenSecondInviteReminder(Recipient recipient) {
|
|
||||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
|
||||||
recipientTable.setSeenSecondInviteReminder(recipient.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPercentOfInsecureMessages(int insecureCount) {
|
|
||||||
MessageTable messageTable = SignalDatabase.messages();
|
|
||||||
int insecure = messageTable.getInsecureMessageCountForInsights();
|
|
||||||
int secure = messageTable.getSecureMessageCountForInsights();
|
|
||||||
|
|
||||||
if (insecure + secure == 0) return 0;
|
|
||||||
return Math.round(100f * (insecureCount / (float) (insecure + secure)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -91,7 +91,7 @@ public final class MmsSendJob extends SendJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Job> compressionJobs = Stream.of(message.getAttachments())
|
List<Job> compressionJobs = Stream.of(message.getAttachments())
|
||||||
.map(a -> (Job) AttachmentCompressionJob.fromAttachment((DatabaseAttachment) a, true, message.getSubscriptionId()))
|
.map(a -> (Job) AttachmentCompressionJob.fromAttachment((DatabaseAttachment) a, true, -1))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
MmsSendJob sendJob = new MmsSendJob(messageId);
|
MmsSendJob sendJob = new MmsSendJob(messageId);
|
||||||
|
@ -139,7 +139,7 @@ public final class MmsSendJob extends SendJob {
|
||||||
validateDestinations(message, pdu);
|
validateDestinations(message, pdu);
|
||||||
|
|
||||||
final byte[] pduBytes = getPduBytes(pdu);
|
final byte[] pduBytes = getPduBytes(pdu);
|
||||||
final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes, message.getSubscriptionId());
|
final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes, -1);
|
||||||
final MmsSendResult result = getSendResult(sendConf, pdu);
|
final MmsSendResult result = getSendResult(sendConf, pdu);
|
||||||
|
|
||||||
database.markAsSent(messageId, false);
|
database.markAsSent(messageId, false);
|
||||||
|
@ -231,7 +231,7 @@ public final class MmsSendJob extends SendJob {
|
||||||
{
|
{
|
||||||
SendReq req = new SendReq();
|
SendReq req = new SendReq();
|
||||||
String lineNumber = getMyNumber(context);
|
String lineNumber = getMyNumber(context);
|
||||||
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(-1);
|
||||||
List<Attachment> scaledAttachments = message.getAttachments();
|
List<Attachment> scaledAttachments = message.getAttachments();
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(lineNumber)) {
|
if (!TextUtils.isEmpty(lineNumber)) {
|
||||||
|
|
|
@ -108,7 +108,7 @@ public class TypingSendJob extends BaseJob {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recipient.isRegistered() || recipient.isForceSmsSelection()) {
|
if (!recipient.isRegistered()) {
|
||||||
Log.w(TAG, "Not sending typing indicators to non-Signal recipients.");
|
Log.w(TAG, "Not sending typing indicators to non-Signal recipients.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -414,8 +414,6 @@ public class MessageContentProcessor {
|
||||||
warn(String.valueOf(content.getTimestamp()), "Got unrecognized message!");
|
warn(String.valueOf(content.getTimestamp()), "Got unrecognized message!");
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRecipientToPush(senderRecipient);
|
|
||||||
|
|
||||||
if (pending != null) {
|
if (pending != null) {
|
||||||
warn(content.getTimestamp(), "Pending retry was processed. Deleting.");
|
warn(content.getTimestamp(), "Pending retry was processed. Deleting.");
|
||||||
ApplicationDependencies.getPendingRetryReceiptCache().delete(pending);
|
ApplicationDependencies.getPendingRetryReceiptCache().delete(pending);
|
||||||
|
@ -2219,7 +2217,6 @@ public class MessageContentProcessor {
|
||||||
body,
|
body,
|
||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
-1,
|
|
||||||
expiresInMillis,
|
expiresInMillis,
|
||||||
false,
|
false,
|
||||||
ThreadTable.DistributionTypes.DEFAULT,
|
ThreadTable.DistributionTypes.DEFAULT,
|
||||||
|
@ -2342,7 +2339,6 @@ public class MessageContentProcessor {
|
||||||
textStoryBody,
|
textStoryBody,
|
||||||
pendingAttachments,
|
pendingAttachments,
|
||||||
sentAtTimestamp,
|
sentAtTimestamp,
|
||||||
-1,
|
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
ThreadTable.DistributionTypes.DEFAULT,
|
ThreadTable.DistributionTypes.DEFAULT,
|
||||||
|
@ -2442,7 +2438,6 @@ public class MessageContentProcessor {
|
||||||
message.getDataMessage().get().getBody().orElse(null),
|
message.getDataMessage().get().getBody().orElse(null),
|
||||||
syncAttachments,
|
syncAttachments,
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
-1,
|
|
||||||
TimeUnit.SECONDS.toMillis(message.getDataMessage().get().getExpiresInSeconds()),
|
TimeUnit.SECONDS.toMillis(message.getDataMessage().get().getExpiresInSeconds()),
|
||||||
viewOnce,
|
viewOnce,
|
||||||
ThreadTable.DistributionTypes.DEFAULT,
|
ThreadTable.DistributionTypes.DEFAULT,
|
||||||
|
@ -2657,7 +2652,6 @@ public class MessageContentProcessor {
|
||||||
new SlideDeck(),
|
new SlideDeck(),
|
||||||
body,
|
body,
|
||||||
message.getTimestamp(),
|
message.getTimestamp(),
|
||||||
-1,
|
|
||||||
expiresInMillis,
|
expiresInMillis,
|
||||||
false,
|
false,
|
||||||
StoryType.NONE,
|
StoryType.NONE,
|
||||||
|
@ -3403,12 +3397,6 @@ public class MessageContentProcessor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetRecipientToPush(@NonNull Recipient recipient) {
|
|
||||||
if (recipient.isForceSmsSelection()) {
|
|
||||||
SignalDatabase.recipients().setForceSmsSelection(recipient.getId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void forceStickerDownloadIfNecessary(long messageId, List<DatabaseAttachment> stickerAttachments) {
|
private void forceStickerDownloadIfNecessary(long messageId, List<DatabaseAttachment> stickerAttachments) {
|
||||||
if (stickerAttachments.isEmpty()) return;
|
if (stickerAttachments.isEmpty()) return;
|
||||||
|
|
||||||
|
|
|
@ -284,12 +284,6 @@ open class MessageContentProcessorV2(private val context: Context) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetRecipientToPush(recipient: Recipient) {
|
|
||||||
if (recipient.isForceSmsSelection) {
|
|
||||||
SignalDatabase.recipients.setForceSmsSelection(recipient.id, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -425,8 +419,6 @@ open class MessageContentProcessorV2(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRecipientToPush(senderRecipient)
|
|
||||||
|
|
||||||
if (pending != null) {
|
if (pending != null) {
|
||||||
warn(envelope.timestamp, "Pending retry was processed. Deleting.")
|
warn(envelope.timestamp, "Pending retry was processed. Deleting.")
|
||||||
ApplicationDependencies.getPendingRetryReceiptCache().delete(pending)
|
ApplicationDependencies.getPendingRetryReceiptCache().delete(pending)
|
||||||
|
|
|
@ -23,7 +23,6 @@ data class OutgoingMessage(
|
||||||
val sentTimeMillis: Long,
|
val sentTimeMillis: Long,
|
||||||
val body: String = "",
|
val body: String = "",
|
||||||
val distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
val distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
||||||
val subscriptionId: Int = -1,
|
|
||||||
val expiresIn: Long = 0L,
|
val expiresIn: Long = 0L,
|
||||||
val isViewOnce: Boolean = false,
|
val isViewOnce: Boolean = false,
|
||||||
val outgoingQuote: QuoteModel? = null,
|
val outgoingQuote: QuoteModel? = null,
|
||||||
|
@ -66,7 +65,6 @@ data class OutgoingMessage(
|
||||||
body: String? = "",
|
body: String? = "",
|
||||||
attachments: List<Attachment> = emptyList(),
|
attachments: List<Attachment> = emptyList(),
|
||||||
timestamp: Long,
|
timestamp: Long,
|
||||||
subscriptionId: Int = -1,
|
|
||||||
expiresIn: Long = 0L,
|
expiresIn: Long = 0L,
|
||||||
viewOnce: Boolean = false,
|
viewOnce: Boolean = false,
|
||||||
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
||||||
|
@ -89,7 +87,6 @@ data class OutgoingMessage(
|
||||||
body = body ?: "",
|
body = body ?: "",
|
||||||
attachments = attachments,
|
attachments = attachments,
|
||||||
sentTimeMillis = timestamp,
|
sentTimeMillis = timestamp,
|
||||||
subscriptionId = subscriptionId,
|
|
||||||
expiresIn = expiresIn,
|
expiresIn = expiresIn,
|
||||||
isViewOnce = viewOnce,
|
isViewOnce = viewOnce,
|
||||||
distributionType = distributionType,
|
distributionType = distributionType,
|
||||||
|
@ -117,7 +114,6 @@ data class OutgoingMessage(
|
||||||
slideDeck: SlideDeck,
|
slideDeck: SlideDeck,
|
||||||
body: String? = "",
|
body: String? = "",
|
||||||
timestamp: Long,
|
timestamp: Long,
|
||||||
subscriptionId: Int = -1,
|
|
||||||
expiresIn: Long = 0L,
|
expiresIn: Long = 0L,
|
||||||
viewOnce: Boolean = false,
|
viewOnce: Boolean = false,
|
||||||
storyType: StoryType = StoryType.NONE,
|
storyType: StoryType = StoryType.NONE,
|
||||||
|
@ -131,7 +127,6 @@ data class OutgoingMessage(
|
||||||
body = buildMessage(slideDeck, body ?: ""),
|
body = buildMessage(slideDeck, body ?: ""),
|
||||||
attachments = slideDeck.asAttachments(),
|
attachments = slideDeck.asAttachments(),
|
||||||
sentTimeMillis = timestamp,
|
sentTimeMillis = timestamp,
|
||||||
subscriptionId = subscriptionId,
|
|
||||||
expiresIn = expiresIn,
|
expiresIn = expiresIn,
|
||||||
isViewOnce = viewOnce,
|
isViewOnce = viewOnce,
|
||||||
storyType = storyType,
|
storyType = storyType,
|
||||||
|
@ -142,6 +137,8 @@ data class OutgoingMessage(
|
||||||
sharedContacts = contacts
|
sharedContacts = contacts
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val subscriptionId = -1
|
||||||
|
|
||||||
fun withExpiry(expiresIn: Long): OutgoingMessage {
|
fun withExpiry(expiresIn: Long): OutgoingMessage {
|
||||||
return copy(expiresIn = expiresIn)
|
return copy(expiresIn = expiresIn)
|
||||||
}
|
}
|
||||||
|
@ -172,12 +169,11 @@ data class OutgoingMessage(
|
||||||
* A literal, insecure SMS message.
|
* A literal, insecure SMS message.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun sms(threadRecipient: Recipient, body: String, subscriptionId: Int): OutgoingMessage {
|
fun sms(threadRecipient: Recipient, body: String): OutgoingMessage {
|
||||||
return OutgoingMessage(
|
return OutgoingMessage(
|
||||||
threadRecipient = threadRecipient,
|
threadRecipient = threadRecipient,
|
||||||
sentTimeMillis = System.currentTimeMillis(),
|
sentTimeMillis = System.currentTimeMillis(),
|
||||||
body = body,
|
body = body,
|
||||||
subscriptionId = subscriptionId,
|
|
||||||
isSecure = false
|
isSecure = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,6 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
long threadId;
|
long threadId;
|
||||||
|
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
int subscriptionId = recipient.getDefaultSubscriptionId().orElse(-1);
|
|
||||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
||||||
ParentStoryId parentStoryId = groupStoryId != Long.MIN_VALUE ? ParentStoryId.deserialize(groupStoryId) : null;
|
ParentStoryId parentStoryId = groupStoryId != Long.MIN_VALUE ? ParentStoryId.deserialize(groupStoryId) : null;
|
||||||
|
|
||||||
|
@ -86,7 +85,6 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
responseText.toString(),
|
responseText.toString(),
|
||||||
new LinkedList<>(),
|
new LinkedList<>(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
subscriptionId,
|
|
||||||
expiresIn,
|
expiresIn,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
|
@ -112,11 +110,6 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||||
threadId = MessageSender.send(context, reply, -1, MessageSender.SendType.SIGNAL, null, null);
|
threadId = MessageSender.send(context, reply, -1, MessageSender.SendType.SIGNAL, null, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UnsecuredSmsMessage: {
|
|
||||||
OutgoingMessage reply = OutgoingMessage.sms(recipient, responseText.toString(), subscriptionId);
|
|
||||||
threadId = MessageSender.send(context, reply, -1, MessageSender.SendType.SMS, null, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw new AssertionError("Unknown Reply method");
|
throw new AssertionError("Unknown Reply method");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
package org.thoughtcrime.securesms.notifications;
|
package org.thoughtcrime.securesms.notifications;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
||||||
public enum ReplyMethod {
|
public enum ReplyMethod {
|
||||||
|
|
||||||
GroupMessage,
|
GroupMessage,
|
||||||
SecureMessage,
|
SecureMessage;
|
||||||
UnsecuredSmsMessage;
|
|
||||||
|
|
||||||
public static @NonNull ReplyMethod forRecipient(Context context, Recipient recipient) {
|
public static @NonNull ReplyMethod forRecipient(Recipient recipient) {
|
||||||
if (recipient.isGroup()) {
|
if (recipient.isGroup()) {
|
||||||
return ReplyMethod.GroupMessage;
|
return ReplyMethod.GroupMessage;
|
||||||
} else if (SignalStore.account().isRegistered() && recipient.getRegistered() == RecipientTable.RegisteredState.REGISTERED && !recipient.isForceSmsSelection()) {
|
|
||||||
return ReplyMethod.SecureMessage;
|
|
||||||
} else {
|
} else {
|
||||||
return ReplyMethod.UnsecuredSmsMessage;
|
return ReplyMethod.SecureMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ sealed class NotificationBuilder(protected val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addActions(ReplyMethod.forRecipient(context, conversation.recipient), conversation)
|
addActions(ReplyMethod.forRecipient(conversation.recipient), conversation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,6 +504,5 @@ private fun ReplyMethod.toLongDescription(): Int {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
ReplyMethod.GroupMessage -> R.string.MessageNotifier_reply
|
ReplyMethod.GroupMessage -> R.string.MessageNotifier_reply
|
||||||
ReplyMethod.SecureMessage -> R.string.MessageNotifier_signal_message
|
ReplyMethod.SecureMessage -> R.string.MessageNotifier_signal_message
|
||||||
ReplyMethod.UnsecuredSmsMessage -> R.string.MessageNotifier_unsecured_sms
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,6 @@ public class Recipient {
|
||||||
private final VibrateState callVibrate;
|
private final VibrateState callVibrate;
|
||||||
private final Uri messageRingtone;
|
private final Uri messageRingtone;
|
||||||
private final Uri callRingtone;
|
private final Uri callRingtone;
|
||||||
private final Optional<Integer> defaultSubscriptionId;
|
|
||||||
private final int expireMessages;
|
private final int expireMessages;
|
||||||
private final RegisteredState registered;
|
private final RegisteredState registered;
|
||||||
private final byte[] profileKey;
|
private final byte[] profileKey;
|
||||||
|
@ -121,9 +120,7 @@ public class Recipient {
|
||||||
private final long lastProfileFetch;
|
private final long lastProfileFetch;
|
||||||
private final String notificationChannel;
|
private final String notificationChannel;
|
||||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
private final boolean forceSmsSelection;
|
|
||||||
private final RecipientRecord.Capabilities capabilities;
|
private final RecipientRecord.Capabilities capabilities;
|
||||||
private final InsightsBannerTier insightsBannerTier;
|
|
||||||
private final byte[] storageId;
|
private final byte[] storageId;
|
||||||
private final MentionSetting mentionSetting;
|
private final MentionSetting mentionSetting;
|
||||||
private final ChatWallpaper wallpaper;
|
private final ChatWallpaper wallpaper;
|
||||||
|
@ -392,8 +389,6 @@ public class Recipient {
|
||||||
this.callVibrate = VibrateState.DEFAULT;
|
this.callVibrate = VibrateState.DEFAULT;
|
||||||
this.messageRingtone = null;
|
this.messageRingtone = null;
|
||||||
this.callRingtone = null;
|
this.callRingtone = null;
|
||||||
this.insightsBannerTier = InsightsBannerTier.TIER_TWO;
|
|
||||||
this.defaultSubscriptionId = Optional.empty();
|
|
||||||
this.expireMessages = 0;
|
this.expireMessages = 0;
|
||||||
this.registered = RegisteredState.UNKNOWN;
|
this.registered = RegisteredState.UNKNOWN;
|
||||||
this.profileKey = null;
|
this.profileKey = null;
|
||||||
|
@ -410,7 +405,6 @@ public class Recipient {
|
||||||
this.lastProfileFetch = 0;
|
this.lastProfileFetch = 0;
|
||||||
this.notificationChannel = null;
|
this.notificationChannel = null;
|
||||||
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
||||||
this.forceSmsSelection = false;
|
|
||||||
this.capabilities = RecipientRecord.Capabilities.UNKNOWN;
|
this.capabilities = RecipientRecord.Capabilities.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
|
@ -450,8 +444,6 @@ public class Recipient {
|
||||||
this.callVibrate = details.callVibrateState;
|
this.callVibrate = details.callVibrateState;
|
||||||
this.messageRingtone = details.messageRingtone;
|
this.messageRingtone = details.messageRingtone;
|
||||||
this.callRingtone = details.callRingtone;
|
this.callRingtone = details.callRingtone;
|
||||||
this.insightsBannerTier = details.insightsBannerTier;
|
|
||||||
this.defaultSubscriptionId = details.defaultSubscriptionId;
|
|
||||||
this.expireMessages = details.expireMessages;
|
this.expireMessages = details.expireMessages;
|
||||||
this.registered = details.registered;
|
this.registered = details.registered;
|
||||||
this.profileKey = details.profileKey;
|
this.profileKey = details.profileKey;
|
||||||
|
@ -468,7 +460,6 @@ public class Recipient {
|
||||||
this.lastProfileFetch = details.lastProfileFetch;
|
this.lastProfileFetch = details.lastProfileFetch;
|
||||||
this.notificationChannel = details.notificationChannel;
|
this.notificationChannel = details.notificationChannel;
|
||||||
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
||||||
this.forceSmsSelection = details.forceSmsSelection;
|
|
||||||
this.capabilities = details.capabilities;
|
this.capabilities = details.capabilities;
|
||||||
this.storageId = details.storageId;
|
this.storageId = details.storageId;
|
||||||
this.mentionSetting = details.mentionSetting;
|
this.mentionSetting = details.mentionSetting;
|
||||||
|
@ -840,10 +831,6 @@ public class Recipient {
|
||||||
return requireSmsAddress();
|
return requireSmsAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Integer> getDefaultSubscriptionId() {
|
|
||||||
return defaultSubscriptionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull ProfileName getProfileName() {
|
public @NonNull ProfileName getProfileName() {
|
||||||
return signalProfileName;
|
return signalProfileName;
|
||||||
}
|
}
|
||||||
|
@ -1029,14 +1016,6 @@ public class Recipient {
|
||||||
return expireMessages;
|
return expireMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSeenFirstInviteReminder() {
|
|
||||||
return insightsBannerTier.seen(InsightsBannerTier.TIER_ONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSeenSecondInviteReminder() {
|
|
||||||
return insightsBannerTier.seen(InsightsBannerTier.TIER_TWO);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull RegisteredState getRegistered() {
|
public @NonNull RegisteredState getRegistered() {
|
||||||
if (isPushGroup() || isDistributionList()) {
|
if (isPushGroup() || isDistributionList()) {
|
||||||
return RegisteredState.REGISTERED;
|
return RegisteredState.REGISTERED;
|
||||||
|
@ -1063,10 +1042,6 @@ public class Recipient {
|
||||||
return !NotificationChannels.supported() ? null : notificationChannel;
|
return !NotificationChannels.supported() ? null : notificationChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isForceSmsSelection() {
|
|
||||||
return forceSmsSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull Capability getStoriesCapability() {
|
public @NonNull Capability getStoriesCapability() {
|
||||||
return capabilities.getStoriesCapability();
|
return capabilities.getStoriesCapability();
|
||||||
}
|
}
|
||||||
|
@ -1353,7 +1328,6 @@ public class Recipient {
|
||||||
Objects.equals(profileAvatarFileDetails, other.profileAvatarFileDetails) &&
|
Objects.equals(profileAvatarFileDetails, other.profileAvatarFileDetails) &&
|
||||||
profileSharing == other.profileSharing &&
|
profileSharing == other.profileSharing &&
|
||||||
isHidden == other.isHidden &&
|
isHidden == other.isHidden &&
|
||||||
forceSmsSelection == other.forceSmsSelection &&
|
|
||||||
Objects.equals(aci, other.aci) &&
|
Objects.equals(aci, other.aci) &&
|
||||||
Objects.equals(username, other.username) &&
|
Objects.equals(username, other.username) &&
|
||||||
Objects.equals(e164, other.e164) &&
|
Objects.equals(e164, other.e164) &&
|
||||||
|
@ -1365,7 +1339,6 @@ public class Recipient {
|
||||||
callVibrate == other.callVibrate &&
|
callVibrate == other.callVibrate &&
|
||||||
Objects.equals(messageRingtone, other.messageRingtone) &&
|
Objects.equals(messageRingtone, other.messageRingtone) &&
|
||||||
Objects.equals(callRingtone, other.callRingtone) &&
|
Objects.equals(callRingtone, other.callRingtone) &&
|
||||||
Objects.equals(defaultSubscriptionId, other.defaultSubscriptionId) &&
|
|
||||||
registered == other.registered &&
|
registered == other.registered &&
|
||||||
Arrays.equals(profileKey, other.profileKey) &&
|
Arrays.equals(profileKey, other.profileKey) &&
|
||||||
Objects.equals(expiringProfileKeyCredential, other.expiringProfileKeyCredential) &&
|
Objects.equals(expiringProfileKeyCredential, other.expiringProfileKeyCredential) &&
|
||||||
|
@ -1378,7 +1351,6 @@ public class Recipient {
|
||||||
Objects.equals(profileAvatar, other.profileAvatar) &&
|
Objects.equals(profileAvatar, other.profileAvatar) &&
|
||||||
Objects.equals(notificationChannel, other.notificationChannel) &&
|
Objects.equals(notificationChannel, other.notificationChannel) &&
|
||||||
unidentifiedAccessMode == other.unidentifiedAccessMode &&
|
unidentifiedAccessMode == other.unidentifiedAccessMode &&
|
||||||
insightsBannerTier == other.insightsBannerTier &&
|
|
||||||
Arrays.equals(storageId, other.storageId) &&
|
Arrays.equals(storageId, other.storageId) &&
|
||||||
mentionSetting == other.mentionSetting &&
|
mentionSetting == other.mentionSetting &&
|
||||||
Objects.equals(wallpaper, other.wallpaper) &&
|
Objects.equals(wallpaper, other.wallpaper) &&
|
||||||
|
|
|
@ -58,7 +58,6 @@ public class RecipientDetails {
|
||||||
final int expireMessages;
|
final int expireMessages;
|
||||||
final List<RecipientId> participantIds;
|
final List<RecipientId> participantIds;
|
||||||
final ProfileName profileName;
|
final ProfileName profileName;
|
||||||
final Optional<Integer> defaultSubscriptionId;
|
|
||||||
final RegisteredState registered;
|
final RegisteredState registered;
|
||||||
final byte[] profileKey;
|
final byte[] profileKey;
|
||||||
final ExpiringProfileKeyCredential expiringProfileKeyCredential;
|
final ExpiringProfileKeyCredential expiringProfileKeyCredential;
|
||||||
|
@ -72,9 +71,7 @@ public class RecipientDetails {
|
||||||
final boolean isSelf;
|
final boolean isSelf;
|
||||||
final String notificationChannel;
|
final String notificationChannel;
|
||||||
final UnidentifiedAccessMode unidentifiedAccessMode;
|
final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
final boolean forceSmsSelection;
|
|
||||||
final RecipientRecord.Capabilities capabilities;
|
final RecipientRecord.Capabilities capabilities;
|
||||||
final InsightsBannerTier insightsBannerTier;
|
|
||||||
final byte[] storageId;
|
final byte[] storageId;
|
||||||
final MentionSetting mentionSetting;
|
final MentionSetting mentionSetting;
|
||||||
final ChatWallpaper wallpaper;
|
final ChatWallpaper wallpaper;
|
||||||
|
@ -125,7 +122,6 @@ public class RecipientDetails {
|
||||||
this.participantIds = participantIds == null ? new LinkedList<>() : participantIds;
|
this.participantIds = participantIds == null ? new LinkedList<>() : participantIds;
|
||||||
this.isActiveGroup = isActiveGroup;
|
this.isActiveGroup = isActiveGroup;
|
||||||
this.profileName = record.getProfileName();
|
this.profileName = record.getProfileName();
|
||||||
this.defaultSubscriptionId = record.getDefaultSubscriptionId();
|
|
||||||
this.registered = registeredState;
|
this.registered = registeredState;
|
||||||
this.profileKey = record.getProfileKey();
|
this.profileKey = record.getProfileKey();
|
||||||
this.expiringProfileKeyCredential = record.getExpiringProfileKeyCredential();
|
this.expiringProfileKeyCredential = record.getExpiringProfileKeyCredential();
|
||||||
|
@ -138,9 +134,7 @@ public class RecipientDetails {
|
||||||
this.isSelf = isSelf;
|
this.isSelf = isSelf;
|
||||||
this.notificationChannel = record.getNotificationChannel();
|
this.notificationChannel = record.getNotificationChannel();
|
||||||
this.unidentifiedAccessMode = record.getUnidentifiedAccessMode();
|
this.unidentifiedAccessMode = record.getUnidentifiedAccessMode();
|
||||||
this.forceSmsSelection = record.isForceSmsSelection();
|
|
||||||
this.capabilities = record.getCapabilities();
|
this.capabilities = record.getCapabilities();
|
||||||
this.insightsBannerTier = record.getInsightsBannerTier();
|
|
||||||
this.storageId = record.getStorageId();
|
this.storageId = record.getStorageId();
|
||||||
this.mentionSetting = record.getMentionSetting();
|
this.mentionSetting = record.getMentionSetting();
|
||||||
this.wallpaper = record.getWallpaper();
|
this.wallpaper = record.getWallpaper();
|
||||||
|
@ -181,8 +175,6 @@ public class RecipientDetails {
|
||||||
this.expireMessages = 0;
|
this.expireMessages = 0;
|
||||||
this.participantIds = new LinkedList<>();
|
this.participantIds = new LinkedList<>();
|
||||||
this.profileName = ProfileName.EMPTY;
|
this.profileName = ProfileName.EMPTY;
|
||||||
this.insightsBannerTier = InsightsBannerTier.TIER_TWO;
|
|
||||||
this.defaultSubscriptionId = Optional.empty();
|
|
||||||
this.registered = RegisteredState.UNKNOWN;
|
this.registered = RegisteredState.UNKNOWN;
|
||||||
this.profileKey = null;
|
this.profileKey = null;
|
||||||
this.expiringProfileKeyCredential = null;
|
this.expiringProfileKeyCredential = null;
|
||||||
|
@ -195,7 +187,6 @@ public class RecipientDetails {
|
||||||
this.isSelf = false;
|
this.isSelf = false;
|
||||||
this.notificationChannel = null;
|
this.notificationChannel = null;
|
||||||
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
||||||
this.forceSmsSelection = false;
|
|
||||||
this.groupName = null;
|
this.groupName = null;
|
||||||
this.capabilities = RecipientRecord.Capabilities.UNKNOWN;
|
this.capabilities = RecipientRecord.Capabilities.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
|
|
|
@ -297,7 +297,6 @@ public class RecipientUtil {
|
||||||
threadRecipient.isProfileSharing() ||
|
threadRecipient.isProfileSharing() ||
|
||||||
threadRecipient.isSystemContact() ||
|
threadRecipient.isSystemContact() ||
|
||||||
!threadRecipient.isRegistered() ||
|
!threadRecipient.isRegistered() ||
|
||||||
threadRecipient.isForceSmsSelection() ||
|
|
||||||
threadRecipient.isHidden();
|
threadRecipient.isHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +345,6 @@ public class RecipientUtil {
|
||||||
threadRecipient.isSelf() ||
|
threadRecipient.isSelf() ||
|
||||||
threadRecipient.isProfileSharing() ||
|
threadRecipient.isProfileSharing() ||
|
||||||
threadRecipient.isSystemContact() ||
|
threadRecipient.isSystemContact() ||
|
||||||
threadRecipient.isForceSmsSelection() ||
|
|
||||||
!threadRecipient.isRegistered() ||
|
!threadRecipient.isRegistered() ||
|
||||||
(!threadRecipient.isHidden() && (
|
(!threadRecipient.isHidden() && (
|
||||||
hasSentMessageInThread(threadId) ||
|
hasSentMessageInThread(threadId) ||
|
||||||
|
|
|
@ -46,11 +46,10 @@ public class QuickResponseService extends IntentService {
|
||||||
number = URLDecoder.decode(number);
|
number = URLDecoder.decode(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient recipient = Recipient.external(this, number);
|
Recipient recipient = Recipient.external(this, number);
|
||||||
int subscriptionId = recipient.getDefaultSubscriptionId().orElse(-1);
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(content)) {
|
if (!TextUtils.isEmpty(content)) {
|
||||||
MessageSender.send(this, OutgoingMessage.sms(recipient, content, subscriptionId), -1, MessageSender.SendType.SIGNAL, null, null);
|
MessageSender.send(this, OutgoingMessage.sms(recipient, content), -1, MessageSender.SendType.SIGNAL, null, null);
|
||||||
}
|
}
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
Toast.makeText(this, R.string.QuickResponseService_problem_sending_message, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.QuickResponseService_problem_sending_message, Toast.LENGTH_LONG).show();
|
||||||
|
|
|
@ -108,9 +108,7 @@ public final class MultiShareSender {
|
||||||
|
|
||||||
long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
|
long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
|
||||||
List<Mention> mentions = getValidMentionsForRecipient(recipient, multiShareArgs.getMentions());
|
List<Mention> mentions = getValidMentionsForRecipient(recipient, multiShareArgs.getMentions());
|
||||||
MessageSendType sendType = resolveTransportOption(context, recipient);
|
MessageSendType sendType = MessageSendType.SignalMessageSendType.INSTANCE;
|
||||||
boolean forceSms = recipient.isForceSmsSelection() && sendType.usesSmsTransport();
|
|
||||||
int subscriptionId = sendType.getSimSubscriptionIdOr(-1);
|
|
||||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
||||||
List<Contact> contacts = multiShareArgs.getSharedContacts();
|
List<Contact> contacts = multiShareArgs.getSharedContacts();
|
||||||
boolean needsSplit = !sendType.usesSmsTransport() &&
|
boolean needsSplit = !sendType.usesSmsTransport() &&
|
||||||
|
@ -139,10 +137,8 @@ public final class MultiShareSender {
|
||||||
slideDeck,
|
slideDeck,
|
||||||
sendType,
|
sendType,
|
||||||
threadId,
|
threadId,
|
||||||
forceSms,
|
|
||||||
expiresIn,
|
expiresIn,
|
||||||
multiShareArgs.isViewOnce(),
|
multiShareArgs.isViewOnce(),
|
||||||
subscriptionId,
|
|
||||||
mentions,
|
mentions,
|
||||||
recipientSearchKey.isStory(),
|
recipientSearchKey.isStory(),
|
||||||
sentTimestamp,
|
sentTimestamp,
|
||||||
|
@ -154,7 +150,7 @@ public final class MultiShareSender {
|
||||||
} else if (recipientSearchKey.isStory()) {
|
} else if (recipientSearchKey.isStory()) {
|
||||||
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.INVALID_SHARE_TO_STORY));
|
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.INVALID_SHARE_TO_STORY));
|
||||||
} else {
|
} else {
|
||||||
sendTextMessage(context, multiShareArgs, recipient, threadId, forceSms, expiresIn, subscriptionId);
|
sendTextMessage(context, multiShareArgs, recipient, threadId, expiresIn);
|
||||||
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.SUCCESS));
|
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.SUCCESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,39 +175,14 @@ public final class MultiShareSender {
|
||||||
return new MultiShareSendResultCollection(results);
|
return new MultiShareSendResultCollection(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull MessageSendType getWorstTransportOption(@NonNull Context context, @NonNull Set<ContactSearchKey.RecipientSearchKey> recipientSearchKeys) {
|
|
||||||
for (ContactSearchKey.RecipientSearchKey recipientSearchKey : recipientSearchKeys) {
|
|
||||||
MessageSendType type = resolveTransportOption(context, Recipient.resolved(recipientSearchKey.getRecipientId()).isForceSmsSelection() && !recipientSearchKey.isStory());
|
|
||||||
if (type.usesSmsTransport()) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MessageSendType.SignalMessageSendType.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @NonNull MessageSendType resolveTransportOption(@NonNull Context context, @NonNull Recipient recipient) {
|
|
||||||
return resolveTransportOption(context, !recipient.isDistributionList() && (recipient.isForceSmsSelection() || !recipient.isRegistered()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NonNull MessageSendType resolveTransportOption(@NonNull Context context, boolean forceSms) {
|
|
||||||
if (forceSms && SignalStore.misc().getSmsExportPhase().allowSmsFeatures()) {
|
|
||||||
return MessageSendType.getFirstForTransport(context, false, MessageSendType.TransportType.SMS);
|
|
||||||
} else {
|
|
||||||
return MessageSendType.SignalMessageSendType.INSTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendMediaMessageOrCollectStoryToBatch(@NonNull Context context,
|
private static void sendMediaMessageOrCollectStoryToBatch(@NonNull Context context,
|
||||||
@NonNull MultiShareArgs multiShareArgs,
|
@NonNull MultiShareArgs multiShareArgs,
|
||||||
@NonNull Recipient recipient,
|
@NonNull Recipient recipient,
|
||||||
@NonNull SlideDeck slideDeck,
|
@NonNull SlideDeck slideDeck,
|
||||||
@NonNull MessageSendType sendType,
|
@NonNull MessageSendType sendType,
|
||||||
long threadId,
|
long threadId,
|
||||||
boolean forceSms,
|
|
||||||
long expiresIn,
|
long expiresIn,
|
||||||
boolean isViewOnce,
|
boolean isViewOnce,
|
||||||
int subscriptionId,
|
|
||||||
@NonNull List<Mention> validatedMentions,
|
@NonNull List<Mention> validatedMentions,
|
||||||
boolean isStory,
|
boolean isStory,
|
||||||
@NonNull MultiShareTimestampProvider sentTimestamps,
|
@NonNull MultiShareTimestampProvider sentTimestamps,
|
||||||
|
@ -221,7 +192,7 @@ public final class MultiShareSender {
|
||||||
@NonNull List<Contact> contacts)
|
@NonNull List<Contact> contacts)
|
||||||
{
|
{
|
||||||
String body = multiShareArgs.getDraftText();
|
String body = multiShareArgs.getDraftText();
|
||||||
if (sendType.usesSignalTransport() && !forceSms && body != null) {
|
if (sendType.usesSignalTransport() && body != null) {
|
||||||
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(context, body, sendType.calculateCharacters(body).maxPrimaryMessageSize);
|
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(context, body, sendType.calculateCharacters(body).maxPrimaryMessageSize);
|
||||||
body = splitMessage.getBody();
|
body = splitMessage.getBody();
|
||||||
|
|
||||||
|
@ -249,7 +220,6 @@ public final class MultiShareSender {
|
||||||
new SlideDeck(),
|
new SlideDeck(),
|
||||||
body,
|
body,
|
||||||
sentTimestamps.getMillis(0),
|
sentTimestamps.getMillis(0),
|
||||||
subscriptionId,
|
|
||||||
0L,
|
0L,
|
||||||
false,
|
false,
|
||||||
storyType.toTextStoryType(),
|
storyType.toTextStoryType(),
|
||||||
|
@ -289,7 +259,6 @@ public final class MultiShareSender {
|
||||||
singletonDeck,
|
singletonDeck,
|
||||||
body,
|
body,
|
||||||
sentTimestamps.getMillis(i),
|
sentTimestamps.getMillis(i),
|
||||||
subscriptionId,
|
|
||||||
0L,
|
0L,
|
||||||
false,
|
false,
|
||||||
storyType,
|
storyType,
|
||||||
|
@ -307,7 +276,6 @@ public final class MultiShareSender {
|
||||||
slideDeck,
|
slideDeck,
|
||||||
body,
|
body,
|
||||||
sentTimestamps.getMillis(0),
|
sentTimestamps.getMillis(0),
|
||||||
subscriptionId,
|
|
||||||
expiresIn,
|
expiresIn,
|
||||||
isViewOnce,
|
isViewOnce,
|
||||||
StoryType.NONE,
|
StoryType.NONE,
|
||||||
|
@ -322,7 +290,7 @@ public final class MultiShareSender {
|
||||||
|
|
||||||
if (isStory) {
|
if (isStory) {
|
||||||
storiesToBatchSend.addAll(outgoingMessages);
|
storiesToBatchSend.addAll(outgoingMessages);
|
||||||
} else if (shouldSendAsPush(recipient, forceSms)) {
|
} else if (shouldSendAsPush(recipient)) {
|
||||||
for (final OutgoingMessage outgoingMessage : outgoingMessages) {
|
for (final OutgoingMessage outgoingMessage : outgoingMessages) {
|
||||||
MessageSender.send(context, outgoingMessage.makeSecure(), threadId, SendType.SIGNAL, null, null);
|
MessageSender.send(context, outgoingMessage.makeSecure(), threadId, SendType.SIGNAL, null, null);
|
||||||
}
|
}
|
||||||
|
@ -399,20 +367,18 @@ public final class MultiShareSender {
|
||||||
@NonNull MultiShareArgs multiShareArgs,
|
@NonNull MultiShareArgs multiShareArgs,
|
||||||
@NonNull Recipient recipient,
|
@NonNull Recipient recipient,
|
||||||
long threadId,
|
long threadId,
|
||||||
boolean forceSms,
|
long expiresIn)
|
||||||
long expiresIn,
|
|
||||||
int subscriptionId)
|
|
||||||
{
|
{
|
||||||
String body = multiShareArgs.getDraftText() == null ? "" : multiShareArgs.getDraftText();
|
String body = multiShareArgs.getDraftText() == null ? "" : multiShareArgs.getDraftText();
|
||||||
|
|
||||||
OutgoingMessage outgoingMessage;
|
OutgoingMessage outgoingMessage;
|
||||||
if (shouldSendAsPush(recipient, forceSms)) {
|
if (shouldSendAsPush(recipient)) {
|
||||||
outgoingMessage = OutgoingMessage.text(recipient, body, expiresIn, System.currentTimeMillis(), multiShareArgs.getBodyRanges());
|
outgoingMessage = OutgoingMessage.text(recipient, body, expiresIn, System.currentTimeMillis(), multiShareArgs.getBodyRanges());
|
||||||
} else {
|
} else {
|
||||||
outgoingMessage = OutgoingMessage.sms(recipient, body, subscriptionId);
|
outgoingMessage = OutgoingMessage.sms(recipient, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageSender.send(context, outgoingMessage, threadId, forceSms ? SendType.SMS : SendType.SIGNAL, null, null);
|
MessageSender.send(context, outgoingMessage, threadId, SendType.SIGNAL, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull OutgoingMessage generateTextStory(@NonNull Context context,
|
private static @NonNull OutgoingMessage generateTextStory(@NonNull Context context,
|
||||||
|
@ -458,10 +424,10 @@ public final class MultiShareSender {
|
||||||
return trimmed.replace(linkPreview.getUrl(), "").trim();
|
return trimmed.replace(linkPreview.getUrl(), "").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldSendAsPush(@NonNull Recipient recipient, boolean forceSms) {
|
private static boolean shouldSendAsPush(@NonNull Recipient recipient) {
|
||||||
return recipient.isDistributionList() ||
|
return recipient.isDistributionList() ||
|
||||||
recipient.isServiceIdOnly() ||
|
recipient.isServiceIdOnly() ||
|
||||||
(recipient.isRegistered() && !forceSms);
|
recipient.isRegistered();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull SlideDeck buildSlideDeck(@NonNull Context context, @NonNull MultiShareArgs multiShareArgs) throws SlideNotFoundException {
|
private static @NonNull SlideDeck buildSlideDeck(@NonNull Context context, @NonNull MultiShareArgs multiShareArgs) throws SlideNotFoundException {
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class ShareInterstitialActivity extends PassphraseRequiredActivity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !recipient.isRegistered() || recipient.isForceSmsSelection();
|
return !recipient.isRegistered();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasSms) {
|
if (hasSms) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.PassphraseRequiredActivity
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||||
|
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFullScreenDialogFragment
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFullScreenDialogFragment
|
||||||
|
@ -271,7 +272,7 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
|
||||||
|
|
||||||
val intent = share(
|
val intent = share(
|
||||||
this,
|
this,
|
||||||
MultiShareSender.getWorstTransportOption(this, multiShareArgs.recipientSearchKeys),
|
MessageSendType.SignalMessageSendType,
|
||||||
media,
|
media,
|
||||||
multiShareArgs.recipientSearchKeys.toList(),
|
multiShareArgs.recipientSearchKeys.toList(),
|
||||||
multiShareArgs.draftText,
|
multiShareArgs.draftText,
|
||||||
|
|
|
@ -1,200 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:viewBindingIgnore="true"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:fillViewport="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/insights_dashboard_this_stat_was_generated_locally"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingTop="?actionBarSize">
|
|
||||||
|
|
||||||
<com.airbnb.lottie.LottieAnimationView
|
|
||||||
android:id="@+id/insights_dashboard_lottie_animation"
|
|
||||||
android:layout_width="0dip"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/insights_dashboard_progress"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/insights_dashboard_progress"
|
|
||||||
app:lottie_rawRes="@raw/lottie_insights_100" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.ArcProgressBar
|
|
||||||
android:id="@+id/insights_dashboard_progress"
|
|
||||||
style="@style/Widget.Signal.ArcProgressBar"
|
|
||||||
android:layout_width="187dp"
|
|
||||||
android:layout_height="187dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
|
||||||
android:id="@+id/insights_dashboard_avatar"
|
|
||||||
android:layout_width="140dp"
|
|
||||||
android:layout_height="140dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/insights_dashboard_progress"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/insights_dashboard_progress" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/insights_dashboard_percent_container"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_avatar">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_percent_secure"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Headline.Insights"
|
|
||||||
tools:text="100" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_percent"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginBottom="1dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/Insights__percent"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.SubHead.Insights" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_encrypted_messages"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:text="@string/InsightsDashboardFragment__encrypted_messages"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.SubHead.Insights"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_percent_container" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_tagline"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Body.Insights"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_encrypted_messages"
|
|
||||||
tools:text="100% of your outgoing messages in the past 7 days were end-to-end encrypted with Signal Protocol." />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/insights_dashboard_start_a_conversation"
|
|
||||||
style="@style/Signal.Widget.Button.Large.Secondary"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="22dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/InsightsDashboardFragment__start_a_conversation"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Caption.Insights"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_tagline" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_make_signal_secure"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/InsightsDashboardFragment__spread_the_word"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.SubHead.Insights"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_tagline" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_invite_your_contacts"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:text="@string/InsightsDashboardFragment__invite_your_contacts"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Body.Insights"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_make_signal_secure" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/insights_dashboard_recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constrainedHeight="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_dashboard_invite_your_contacts"
|
|
||||||
tools:itemCount="10"
|
|
||||||
tools:listitem="@layout/insights_dashboard_adapter_item" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/insights_dashboard_toolbar"
|
|
||||||
style="?actionBarStyle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:background="?android:windowBackground"
|
|
||||||
android:theme="?actionBarStyle"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:navigationIcon="@drawable/ic_x_tinted"
|
|
||||||
app:title="@string/InsightsDashboardFragment__title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_dashboard_this_stat_was_generated_locally"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/signal_background_secondary"
|
|
||||||
android:gravity="center"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingTop="24dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingBottom="24dp"
|
|
||||||
android:text="@string/InsightsDashboardFragment__this_stat_was_generated_locally"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Body2.Insights"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
tools:viewBindingIgnore="true"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="68dp">
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
|
||||||
android:id="@+id/recipient_avatar"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
|
||||||
android:id="@+id/recipient_display_name"
|
|
||||||
android:layout_width="0dip"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="9dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Title.Insights"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/recipient_invite"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/recipient_avatar"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:text="Peter Parker (This is a long name to make sure we stick within our bounds)" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/recipient_invite"
|
|
||||||
style="@style/Signal.Widget.Button.Large.Secondary"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:text="@string/conversation_insecure__invite"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Caption.Insights"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,115 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:viewBindingIgnore="true"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/insight_modal_background"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/insights_modal_close"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:tint="@color/signal_icon_tint_primary"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_x" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_modal_title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/insights_modal_title_margin_top"
|
|
||||||
android:text="@string/InsightsModalFragment__title"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.SubHead.Insights"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_modal_description"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="@dimen/insights_modal_description_margin_top"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:text="@string/InsightsModalFragment__description"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Title.Insights"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_modal_title" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.ArcProgressBar
|
|
||||||
android:id="@+id/insights_modal_progress"
|
|
||||||
style="@style/Widget.Signal.ArcProgressBar"
|
|
||||||
android:layout_width="@dimen/insights_modal_progress_size"
|
|
||||||
android:layout_height="@dimen/insights_modal_progress_size"
|
|
||||||
android:layout_marginTop="@dimen/insights_modal_progress_margin_top"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_modal_description" />
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
|
||||||
android:id="@+id/insights_modal_avatar"
|
|
||||||
android:layout_width="@dimen/insights_modal_avatar_size"
|
|
||||||
android:layout_height="@dimen/insights_modal_avatar_size"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/insights_modal_progress"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/insights_modal_progress" />
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/insights_modal_percent_container"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_modal_avatar">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_modal_percent_secure"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Headline.Insights.Modal.Percent"
|
|
||||||
tools:text="100" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/insights_modal_percent"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/Insights__percent"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.SubHead.Insights.Modal.Percent" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/insights_modal_view_insights"
|
|
||||||
style="@style/Widget.AppCompat.Button.Borderless"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="@dimen/insights_modal_view_insights_margin_top"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:background="@drawable/insights_cta_button_background"
|
|
||||||
android:text="@string/InsightsModalFragment__view_insights"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Caption.Insights"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/insights_modal_progress" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -26,8 +26,4 @@
|
||||||
<item android:title="@string/ConversationListFragment__notification_profile"
|
<item android:title="@string/ConversationListFragment__notification_profile"
|
||||||
android:id="@+id/menu_notification_profile" />
|
android:id="@+id/menu_notification_profile" />
|
||||||
|
|
||||||
<item android:title="@string/Insights__title"
|
|
||||||
android:id="@+id/menu_insights"
|
|
||||||
android:visible="false" />
|
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
<item type="id" name="contact_info_tag"/>
|
<item type="id" name="contact_info_tag"/>
|
||||||
<item type="id" name="motion_view_edittext"/>
|
<item type="id" name="motion_view_edittext"/>
|
||||||
|
|
||||||
<item name="reminder_action_view_insights" type="id" />
|
|
||||||
<item name="reminder_action_invite" type="id" />
|
<item name="reminder_action_invite" type="id" />
|
||||||
<item name="reminder_action_update_now" type="id" />
|
<item name="reminder_action_update_now" type="id" />
|
||||||
<item name="reminder_action_re_register" type="id" />
|
<item name="reminder_action_re_register" type="id" />
|
||||||
|
|
|
@ -3445,33 +3445,6 @@
|
||||||
<string name="new_conversation_activity__refresh">Refresh</string>
|
<string name="new_conversation_activity__refresh">Refresh</string>
|
||||||
<!-- redphone_audio_popup_menu -->
|
<!-- redphone_audio_popup_menu -->
|
||||||
|
|
||||||
<!-- Insights -->
|
|
||||||
<string name="Insights__percent">%</string>
|
|
||||||
<string name="Insights__title">Insights</string>
|
|
||||||
<string name="InsightsDashboardFragment__title">Insights</string>
|
|
||||||
<!-- Body text for a page describing how many signal messages you\'ve sent. The first placeholder represents the number of signal messages sent, and the second placeholder represents how many days have passed. -->
|
|
||||||
<string name="InsightsDashboardFragment__signal_protocol_automatically_protected">Signal Protocol automatically protected %1$d%% of your outgoing messages over the past %2$d days. Chats between Signal users are always end-to-end encrypted.</string>
|
|
||||||
<string name="InsightsDashboardFragment__spread_the_word">Spread the word</string>
|
|
||||||
<string name="InsightsDashboardFragment__not_enough_data">Not enough data</string>
|
|
||||||
<string name="InsightsDashboardFragment__your_insights_percentage_is_calculated_based_on">Your Insights percentage is calculated based on outgoing messages within the past %1$d days that have not disappeared or been deleted.</string>
|
|
||||||
<!-- Label for a button that, when pressed, will begin a chat with a person whose name is showed on the screen. -->
|
|
||||||
<string name="InsightsDashboardFragment__start_a_conversation">Start a chat</string>
|
|
||||||
<string name="InsightsDashboardFragment__invite_your_contacts">Start communicating securely and enable new features that go beyond the limitations of unencrypted SMS messages by inviting more contacts to join Signal.</string>
|
|
||||||
<string name="InsightsDashboardFragment__this_stat_was_generated_locally">These statistics were locally generated on your device and can only be seen by you. They are never transmitted anywhere.</string>
|
|
||||||
<string name="InsightsDashboardFragment__encrypted_messages">Encrypted messages</string>
|
|
||||||
<string name="InsightsDashboardFragment__cancel">Cancel</string>
|
|
||||||
<string name="InsightsDashboardFragment__send">Send</string>
|
|
||||||
<string name="InsightsModalFragment__title">Introducing Insights</string>
|
|
||||||
<string name="InsightsModalFragment__description">Find out how many of your outgoing messages were sent securely, then quickly invite new contacts to boost your Signal percentage.</string>
|
|
||||||
<string name="InsightsModalFragment__view_insights">View Insights</string>
|
|
||||||
|
|
||||||
<string name="FirstInviteReminder__title">Invite to Signal</string>
|
|
||||||
<string name="FirstInviteReminder__description">You could increase the number of encrypted messages you send by %1$d%%</string>
|
|
||||||
<string name="SecondInviteReminder__title">Boost your Signal</string>
|
|
||||||
<string name="SecondInviteReminder__description">Invite %1$s</string>
|
|
||||||
<string name="InsightsReminder__view_insights">View Insights</string>
|
|
||||||
<string name="InsightsReminder__invite">Invite</string>
|
|
||||||
|
|
||||||
<!-- Edit KBS Pin -->
|
<!-- Edit KBS Pin -->
|
||||||
|
|
||||||
<!-- BaseKbsPinFragment -->
|
<!-- BaseKbsPinFragment -->
|
||||||
|
|
|
@ -46,7 +46,6 @@ object RecipientDatabaseTestUtils {
|
||||||
callVibrateState: RecipientTable.VibrateState = RecipientTable.VibrateState.DEFAULT,
|
callVibrateState: RecipientTable.VibrateState = RecipientTable.VibrateState.DEFAULT,
|
||||||
messageRingtone: Uri = Uri.EMPTY,
|
messageRingtone: Uri = Uri.EMPTY,
|
||||||
callRingtone: Uri = Uri.EMPTY,
|
callRingtone: Uri = Uri.EMPTY,
|
||||||
defaultSubscriptionId: Int = 0,
|
|
||||||
expireMessages: Int = 0,
|
expireMessages: Int = 0,
|
||||||
registered: RecipientTable.RegisteredState = RecipientTable.RegisteredState.REGISTERED,
|
registered: RecipientTable.RegisteredState = RecipientTable.RegisteredState.REGISTERED,
|
||||||
profileKey: ByteArray = Random.nextBytes(32),
|
profileKey: ByteArray = Random.nextBytes(32),
|
||||||
|
@ -63,9 +62,7 @@ object RecipientDatabaseTestUtils {
|
||||||
lastProfileFetch: Long = 0L,
|
lastProfileFetch: Long = 0L,
|
||||||
notificationChannel: String? = null,
|
notificationChannel: String? = null,
|
||||||
unidentifiedAccessMode: RecipientTable.UnidentifiedAccessMode = RecipientTable.UnidentifiedAccessMode.UNKNOWN,
|
unidentifiedAccessMode: RecipientTable.UnidentifiedAccessMode = RecipientTable.UnidentifiedAccessMode.UNKNOWN,
|
||||||
forceSmsSelection: Boolean = false,
|
|
||||||
capabilities: Long = 0L,
|
capabilities: Long = 0L,
|
||||||
insightBannerTier: RecipientTable.InsightsBannerTier = RecipientTable.InsightsBannerTier.NO_TIER,
|
|
||||||
storageId: ByteArray? = null,
|
storageId: ByteArray? = null,
|
||||||
mentionSetting: RecipientTable.MentionSetting = RecipientTable.MentionSetting.ALWAYS_NOTIFY,
|
mentionSetting: RecipientTable.MentionSetting = RecipientTable.MentionSetting.ALWAYS_NOTIFY,
|
||||||
wallpaper: ChatWallpaper? = null,
|
wallpaper: ChatWallpaper? = null,
|
||||||
|
@ -114,7 +111,6 @@ object RecipientDatabaseTestUtils {
|
||||||
callVibrateState,
|
callVibrateState,
|
||||||
messageRingtone,
|
messageRingtone,
|
||||||
callRingtone,
|
callRingtone,
|
||||||
defaultSubscriptionId,
|
|
||||||
expireMessages,
|
expireMessages,
|
||||||
registered,
|
registered,
|
||||||
profileKey,
|
profileKey,
|
||||||
|
@ -131,7 +127,6 @@ object RecipientDatabaseTestUtils {
|
||||||
lastProfileFetch,
|
lastProfileFetch,
|
||||||
notificationChannel,
|
notificationChannel,
|
||||||
unidentifiedAccessMode,
|
unidentifiedAccessMode,
|
||||||
forceSmsSelection,
|
|
||||||
RecipientRecord.Capabilities(
|
RecipientRecord.Capabilities(
|
||||||
capabilities,
|
capabilities,
|
||||||
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.GROUPS_V1_MIGRATION, RecipientTable.Capabilities.BIT_LENGTH).toInt()),
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.GROUPS_V1_MIGRATION, RecipientTable.Capabilities.BIT_LENGTH).toInt()),
|
||||||
|
@ -143,7 +138,6 @@ object RecipientDatabaseTestUtils {
|
||||||
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.PNP, RecipientTable.Capabilities.BIT_LENGTH).toInt()),
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.PNP, RecipientTable.Capabilities.BIT_LENGTH).toInt()),
|
||||||
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.PAYMENT_ACTIVATION, RecipientTable.Capabilities.BIT_LENGTH).toInt())
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.PAYMENT_ACTIVATION, RecipientTable.Capabilities.BIT_LENGTH).toInt())
|
||||||
),
|
),
|
||||||
insightBannerTier,
|
|
||||||
storageId,
|
storageId,
|
||||||
mentionSetting,
|
mentionSetting,
|
||||||
wallpaper,
|
wallpaper,
|
||||||
|
|
|
@ -20,7 +20,6 @@ object TestMms {
|
||||||
body: String = "body",
|
body: String = "body",
|
||||||
sentTimeMillis: Long = System.currentTimeMillis(),
|
sentTimeMillis: Long = System.currentTimeMillis(),
|
||||||
receivedTimestampMillis: Long = System.currentTimeMillis(),
|
receivedTimestampMillis: Long = System.currentTimeMillis(),
|
||||||
subscriptionId: Int = -1,
|
|
||||||
expiresIn: Long = 0,
|
expiresIn: Long = 0,
|
||||||
viewOnce: Boolean = false,
|
viewOnce: Boolean = false,
|
||||||
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
distributionType: Int = ThreadTable.DistributionTypes.DEFAULT,
|
||||||
|
@ -35,7 +34,6 @@ object TestMms {
|
||||||
body,
|
body,
|
||||||
emptyList(),
|
emptyList(),
|
||||||
sentTimeMillis,
|
sentTimeMillis,
|
||||||
subscriptionId,
|
|
||||||
expiresIn,
|
expiresIn,
|
||||||
viewOnce,
|
viewOnce,
|
||||||
distributionType,
|
distributionType,
|
||||||
|
|
Loading…
Add table
Reference in a new issue