diff --git a/res/layout/conversation_item_received.xml b/res/layout/conversation_item_received.xml index fef27a8305..bb58816d13 100644 --- a/res/layout/conversation_item_received.xml +++ b/res/layout/conversation_item_received.xml @@ -113,6 +113,8 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingTop="2dp" + android:paddingRight="4dp" + android:paddingEnd="4dp" android:src="?menu_lock_icon_small" android:contentDescription="@string/conversation_item__secure_message_description" android:visibility="gone" @@ -126,6 +128,19 @@ android:visibility="gone"/> + + + android:linksClickable="false" + android:visibility="gone" + tools:visibility="visible" + tools:text="from SIM1"/> diff --git a/res/layout/conversation_item_sent.xml b/res/layout/conversation_item_sent.xml index 9c8097a7dc..5cb9cf0065 100644 --- a/res/layout/conversation_item_sent.xml +++ b/res/layout/conversation_item_sent.xml @@ -125,6 +125,25 @@ android:paddingBottom="2dp" tools:text="30 mins" /> + + + - + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index a43d874bd9..7c43d814fb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -100,6 +100,8 @@ Fallback to unencrypted MMS? This message will not be encrypted because the recipient is no longer a Signal user.\n\nSend unsecured message? Can\'t find an app able to open this media. + from %s + to %s Reset secure session? diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index bfdb3a65be..1f473a117c 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -535,7 +535,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity final Context context = getApplicationContext(); OutgoingEndSessionMessage endSessionMessage = - new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipients(), "TERMINATE")); + new OutgoingEndSessionMessage(new OutgoingTextMessage(getRecipients(), "TERMINATE", -1)); new AsyncTask() { @Override @@ -823,22 +823,43 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void onSecurityUpdated() { - updateInviteReminder(); + updateRecipientPreferences(); } - protected void updateInviteReminder() { - if (TextSecurePreferences.isPushRegistered(this) && - !isSecureText && - recipients.isSingleRecipient() && - recipients.getPrimaryRecipient() != null && + private void updateRecipientPreferences() { + if (recipients.getPrimaryRecipient() != null && recipients.getPrimaryRecipient().getContactUri() != null) { - new ShowInviteReminderTask().execute(recipients); + new RecipientPreferencesTask().execute(recipients); + } + } + + protected void updateInviteReminder(boolean seenInvite) { + Log.w(TAG, "updateInviteReminder(" + seenInvite+")"); + if (TextSecurePreferences.isPushRegistered(this) && + !isSecureText && + !seenInvite && + recipients.isSingleRecipient()) + { + InviteReminder reminder = new InviteReminder(this, recipients); + reminder.setOkListener(new OnClickListener() { + @Override + public void onClick(View v) { + handleInviteLink(); + reminderView.requestDismiss(); + } + }); + reminderView.showReminder(reminder); } else { reminderView.hide(); } } + private void updateDefaultSubscriptionId(Optional defaultSubscriptionId) { + Log.w(TAG, "updateDefaultSubscriptionId(" + defaultSubscriptionId.orNull() + ")"); + sendButton.setDefaultSubscriptionId(defaultSubscriptionId); + } + private void initializeMmsEnabledCheck() { new AsyncTask() { @Override @@ -896,11 +917,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity sendButton.setEnabled(true); sendButton.addOnTransportChangedListener(new OnTransportChangedListener() { @Override - public void onChange(TransportOption newTransport) { + public void onChange(TransportOption newTransport, boolean manuallySelected) { calculateCharactersRemaining(); composeText.setTransport(newTransport); buttonToggle.getBackground().setColorFilter(newTransport.getBackgroundColor(), Mode.MULTIPLY); buttonToggle.getBackground().invalidateSelf(); + if (manuallySelected) recordSubscriptionIdPreference(newTransport.getSimSubscriptionId()); } }); @@ -968,7 +990,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity titleView.setTitle(recipients); setBlockedUserState(recipients); setActionBarColor(recipients.getColor()); - updateInviteReminder(); + updateRecipientPreferences(); } }); } @@ -1242,8 +1264,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void sendMessage() { try { - Recipients recipients = getRecipients(); - boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); + Recipients recipients = getRecipients(); + boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); Log.w(TAG, "isManual Selection: " + sendButton.isManualSelection()); Log.w(TAG, "forceSms: " + forceSms); @@ -1255,9 +1278,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if ((!recipients.isSingleRecipient() || recipients.isEmailRecipient()) && !isMmsEnabled) { handleManualMmsRequired(); } else if (attachmentManager.isAttachmentPresent() || !recipients.isSingleRecipient() || recipients.isGroupRecipient() || recipients.isEmailRecipient()) { - sendMediaMessage(forceSms); + sendMediaMessage(forceSms, subscriptionId); } else { - sendTextMessage(forceSms); + sendTextMessage(forceSms, subscriptionId); } } catch (RecipientFormattingException ex) { Toast.makeText(ConversationActivity.this, @@ -1271,13 +1294,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } } - private void sendMediaMessage(final boolean forceSms) + private void sendMediaMessage(final boolean forceSms, final int subscriptionId) throws InvalidMessageException { - sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck()); + sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), subscriptionId); } - private ListenableFuture sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck) + private ListenableFuture sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck, final int subscriptionId) throws InvalidMessageException { final SettableFuture future = new SettableFuture<>(); @@ -1286,6 +1309,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity slideDeck, body, System.currentTimeMillis(), + subscriptionId, distributionType); if (isSecureText && !forceSms) { @@ -1311,7 +1335,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity return future; } - private void sendTextMessage(final boolean forceSms) + private void sendTextMessage(final boolean forceSms, final int subscriptionId) throws InvalidMessageException { final Context context = getApplicationContext(); @@ -1320,7 +1344,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (isSecureText && !forceSms) { message = new OutgoingEncryptedMessage(recipients, getMessage()); } else { - message = new OutgoingTextMessage(recipients, getMessage()); + message = new OutgoingTextMessage(recipients, getMessage(), subscriptionId); } this.composeText.setText(""); @@ -1348,6 +1372,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } } + private void recordSubscriptionIdPreference(final Optional subscriptionId) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) + .setDefaultSubscriptionId(recipients, subscriptionId.or(-1)); + return null; + } + }.execute(); + } + @Override public void onAttachmentDrawerStateChanged(DrawerState drawerState) { if (drawerState == DrawerState.FULL_EXPANDED) { @@ -1398,12 +1433,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void onSuccess(final @NonNull Pair result) { try { - boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); - AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, ContentType.AUDIO_AAC); - SlideDeck slideDeck = new SlideDeck(); + boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms(); + int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1); + AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, ContentType.AUDIO_AAC); + SlideDeck slideDeck = new SlideDeck(); slideDeck.addSlide(audioSlide); - sendMediaMessage(forceSms, "", slideDeck).addListener(new AssertedSuccessListener() { + sendMediaMessage(forceSms, "", slideDeck, subscriptionId).addListener(new AssertedSuccessListener() { @Override public void onSuccess(Void nothing) { new AsyncTask() { @@ -1568,30 +1604,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity updateToggleButtonState(); } - private class ShowInviteReminderTask extends AsyncTask> { + private class RecipientPreferencesTask extends AsyncTask> { @Override - protected Pair doInBackground(Recipients... recipients) { - if (recipients.length != 1 || recipients[0] == null) throw new AssertionError("task needs exactly one Recipients object"); + protected Pair doInBackground(Recipients... recipients) { + if (recipients.length != 1 || recipients[0] == null) { + throw new AssertionError("task needs exactly one Recipients object"); + } Optional prefs = DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this) .getRecipientsPreferences(recipients[0].getIds()); - return new Pair<>(recipients[0], prefs.isPresent() && prefs.get().hasSeenInviteReminder()); + return new Pair<>(recipients[0], prefs.orNull()); } @Override - protected void onPostExecute(Pair result) { - if (!result.second && result.first == recipients) { - InviteReminder reminder = new InviteReminder(ConversationActivity.this, result.first); - reminder.setOkListener(new OnClickListener() { - @Override - public void onClick(View v) { - handleInviteLink(); - reminderView.requestDismiss(); - } - }); - reminderView.showReminder(reminder); - } else { - reminderView.hide(); + protected void onPostExecute(@NonNull Pair result) { + if (result.first == recipients) { + updateInviteReminder(result.second != null && result.second.hasSeenInviteReminder()); + updateDefaultSubscriptionId(result.second != null ? result.second.getDefaultSubscriptionId() : Optional.absent()); } } } diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 08debe8c95..5186263b9c 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -65,6 +65,9 @@ import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat; +import org.whispersystems.libaxolotl.util.guava.Optional; import java.util.HashSet; import java.util.List; @@ -93,6 +96,7 @@ public class ConversationItem extends LinearLayout private View bodyBubble; private TextView bodyText; private TextView dateText; + private TextView simInfoText; private TextView indicatorText; private TextView groupStatusText; private ImageView secureImage; @@ -135,6 +139,7 @@ public class ConversationItem extends LinearLayout this.bodyText = (TextView) findViewById(R.id.conversation_item_body); this.dateText = (TextView) findViewById(R.id.conversation_item_date); + this.simInfoText = (TextView) findViewById(R.id.sim_info); this.indicatorText = (TextView) findViewById(R.id.indicator_text); this.groupStatusText = (TextView) findViewById(R.id.group_message_status); this.secureImage = (ImageView) findViewById(R.id.secure_indicator); @@ -188,6 +193,7 @@ public class ConversationItem extends LinearLayout setGroupMessageStatus(messageRecord, recipient); setMinimumWidth(); setMediaAttributes(messageRecord); + setSimInfo(messageRecord); } private void initializeAttributes() { @@ -327,6 +333,25 @@ public class ConversationItem extends LinearLayout } } + private void setSimInfo(MessageRecord messageRecord) { + if (messageRecord.getSubscriptionId() == -1) { + simInfoText.setVisibility(View.GONE); + } else { + SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(context); + Optional subscriptionInfo = subscriptionManager.getActiveSubscriptionInfo(messageRecord.getSubscriptionId()); + + if (subscriptionInfo.isPresent() && messageRecord.isOutgoing()) { + simInfoText.setText(getContext().getString(R.string.ConversationItem_from_s, subscriptionInfo.get().getDisplayName())); + simInfoText.setVisibility(View.VISIBLE); + } else if (subscriptionInfo.isPresent()) { + simInfoText.setText(getContext().getString(R.string.ConversationItem_to_s, subscriptionInfo.get().getDisplayName())); + simInfoText.setVisibility(View.VISIBLE); + } else { + simInfoText.setVisibility(View.GONE); + } + } + } + private void setFailedStatusIcons() { alertView.setFailed(); deliveryStatusIndicator.setNone(); diff --git a/src/org/thoughtcrime/securesms/ConversationPopupActivity.java b/src/org/thoughtcrime/securesms/ConversationPopupActivity.java index 3c34b85f7a..e0ba797db6 100644 --- a/src/org/thoughtcrime/securesms/ConversationPopupActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationPopupActivity.java @@ -121,7 +121,7 @@ public class ConversationPopupActivity extends ConversationActivity { } @Override - protected void updateInviteReminder() { + protected void updateInviteReminder(boolean seenInvite) { reminderView.setVisibility(View.GONE); } } diff --git a/src/org/thoughtcrime/securesms/InviteActivity.java b/src/org/thoughtcrime/securesms/InviteActivity.java index bf3b79754a..b16c73c749 100644 --- a/src/org/thoughtcrime/securesms/InviteActivity.java +++ b/src/org/thoughtcrime/securesms/InviteActivity.java @@ -28,13 +28,15 @@ import org.thoughtcrime.securesms.components.ContactFilterToolbar; import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; -import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener; +import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask; +import org.whispersystems.libaxolotl.util.guava.Optional; import java.util.concurrent.ExecutionException; @@ -227,9 +229,14 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen if (context == null) return null; for (String number : numbers) { - final Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false); - if (recipients != null && recipients.getPrimaryRecipient() != null) { - MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message), -1L, true); + Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false); + + if (recipients.getPrimaryRecipient() != null) { + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getIds()); + int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; + + MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message, subscriptionId), -1L, true); + if (recipients.getPrimaryRecipient().getContactUri() != null) { DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true); } diff --git a/src/org/thoughtcrime/securesms/TransportOption.java b/src/org/thoughtcrime/securesms/TransportOption.java index 875f1114ad..7d91d1c04a 100644 --- a/src/org/thoughtcrime/securesms/TransportOption.java +++ b/src/org/thoughtcrime/securesms/TransportOption.java @@ -1,9 +1,11 @@ package org.thoughtcrime.securesms; import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; import org.thoughtcrime.securesms.util.CharacterCalculator; import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState; +import org.whispersystems.libaxolotl.util.guava.Optional; public class TransportOption { @@ -12,19 +14,34 @@ public class TransportOption { TEXTSECURE } - private int drawable; - private int backgroundColor; - private String text; - private Type type; - private String composeHint; - private CharacterCalculator characterCalculator; + private final int drawable; + private final int backgroundColor; + private final @NonNull String text; + private final @NonNull Type type; + private final @NonNull String composeHint; + private final @NonNull CharacterCalculator characterCalculator; + private final @NonNull Optional simName; + private final @NonNull Optional simSubscriptionId; - public TransportOption(Type type, + public TransportOption(@NonNull Type type, @DrawableRes int drawable, int backgroundColor, - String text, - String composeHint, - CharacterCalculator characterCalculator) + @NonNull String text, + @NonNull String composeHint, + @NonNull CharacterCalculator characterCalculator) + { + this(type, drawable, backgroundColor, text, composeHint, characterCalculator, + Optional.absent(), Optional.absent()); + } + + public TransportOption(@NonNull Type type, + @DrawableRes int drawable, + int backgroundColor, + @NonNull String text, + @NonNull String composeHint, + @NonNull CharacterCalculator characterCalculator, + @NonNull Optional simName, + @NonNull Optional simSubscriptionId) { this.type = type; this.drawable = drawable; @@ -32,9 +49,12 @@ public class TransportOption { this.text = text; this.composeHint = composeHint; this.characterCalculator = characterCalculator; + this.simName = simName; + this.simSubscriptionId = simSubscriptionId; } - public Type getType() { + + public @NonNull Type getType() { return type; } @@ -58,11 +78,22 @@ public class TransportOption { return backgroundColor; } - public String getComposeHint() { + public @NonNull String getComposeHint() { return composeHint; } - public String getDescription() { + public @NonNull String getDescription() { return text; } + + @NonNull + public Optional getSimName() { + return simName; + } + + @NonNull + public Optional getSimSubscriptionId() { + return simSubscriptionId; + } + } diff --git a/src/org/thoughtcrime/securesms/TransportOptions.java b/src/org/thoughtcrime/securesms/TransportOptions.java index c78d3986e7..ccbc66143c 100644 --- a/src/org/thoughtcrime/securesms/TransportOptions.java +++ b/src/org/thoughtcrime/securesms/TransportOptions.java @@ -1,10 +1,15 @@ package org.thoughtcrime.securesms; import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import org.thoughtcrime.securesms.util.CharacterCalculator; import org.thoughtcrime.securesms.util.MmsCharacterCalculator; import org.thoughtcrime.securesms.util.PushCharacterCalculator; import org.thoughtcrime.securesms.util.SmsCharacterCalculator; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat; +import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat; import org.whispersystems.libaxolotl.util.guava.Optional; import java.util.LinkedList; @@ -20,49 +25,76 @@ public class TransportOptions { private final Context context; private final List enabledTransports; - private Type selectedType; - private boolean manuallySelected; + private Type defaultTransportType = Type.SMS; + private Optional defaultSubscriptionId = Optional.absent(); + private Optional selectedOption = Optional.absent(); public TransportOptions(Context context, boolean media) { this.context = context; this.enabledTransports = initializeAvailableTransports(media); - - setDefaultTransport(Type.SMS); } public void reset(boolean media) { List transportOptions = initializeAvailableTransports(media); + this.enabledTransports.clear(); this.enabledTransports.addAll(transportOptions); - if (!find(selectedType).isPresent()) { - this.manuallySelected = false; - setTransport(Type.SMS); + if (selectedOption.isPresent() && !isEnabled(selectedOption.get())) { + setSelectedTransport(null); } else { + this.defaultTransportType = Type.SMS; + this.defaultSubscriptionId = Optional.absent(); + notifyTransportChangeListeners(); } } public void setDefaultTransport(Type type) { - if (!this.manuallySelected) { - setTransport(type); + this.defaultTransportType = type; + + if (!selectedOption.isPresent()) { + notifyTransportChangeListeners(); } } - public void setSelectedTransport(Type type) { - this.manuallySelected= true; - setTransport(type); + public void setDefaultSubscriptionId(Optional subscriptionId) { + this.defaultSubscriptionId = subscriptionId; + + if (!selectedOption.isPresent()) { + notifyTransportChangeListeners(); + } + } + + public void setSelectedTransport(@Nullable TransportOption transportOption) { + this.selectedOption = Optional.fromNullable(transportOption); + notifyTransportChangeListeners(); } public boolean isManualSelection() { - return manuallySelected; + return this.selectedOption.isPresent(); } - public TransportOption getSelectedTransport() { - Optional option = find(selectedType); + public @NonNull TransportOption getSelectedTransport() { + if (selectedOption.isPresent()) return selectedOption.get(); - if (option.isPresent()) return option.get(); - else throw new AssertionError("Selected type isn't present!"); + if (defaultSubscriptionId.isPresent()) { + for (TransportOption transportOption : enabledTransports) { + if (transportOption.getType() == defaultTransportType && + (int)defaultSubscriptionId.get() == transportOption.getSimSubscriptionId().or(-1)) + { + return transportOption; + } + } + } + + for (TransportOption transportOption : enabledTransports) { + if (transportOption.getType() == defaultTransportType) { + return transportOption; + } + } + + throw new AssertionError("No options of default type!"); } public void disableTransport(Type type) { @@ -71,8 +103,8 @@ public class TransportOptions { if (option.isPresent()) { enabledTransports.remove(option.get()); - if (manuallySelected && type == selectedType) { - manuallySelected = false; + if (selectedOption.isPresent() && selectedOption.get().getType() == type) { + setSelectedTransport(null); } } } @@ -89,17 +121,13 @@ public class TransportOptions { List results = new LinkedList<>(); if (isMediaMessage) { - results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp, - context.getResources().getColor(R.color.grey_600), - context.getString(R.string.ConversationActivity_transport_insecure_mms), - context.getString(R.string.conversation_activity__type_message_mms_insecure), - new MmsCharacterCalculator())); + results.addAll(getTransportOptionsForSimCards(context.getString(R.string.ConversationActivity_transport_insecure_mms), + context.getString(R.string.conversation_activity__type_message_mms_insecure), + new MmsCharacterCalculator())); } else { - results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp, - context.getResources().getColor(R.color.grey_600), - context.getString(R.string.ConversationActivity_transport_insecure_sms), - context.getString(R.string.conversation_activity__type_message_sms_insecure), - new SmsCharacterCalculator())); + results.addAll(getTransportOptionsForSimCards(context.getString(R.string.ConversationActivity_transport_insecure_sms), + context.getString(R.string.conversation_activity__type_message_sms_insecure), + new SmsCharacterCalculator())); } results.add(new TransportOption(Type.TEXTSECURE, R.drawable.ic_send_push_white_24dp, @@ -111,16 +139,34 @@ public class TransportOptions { return results; } + private @NonNull List getTransportOptionsForSimCards(@NonNull String text, + @NonNull String composeHint, + @NonNull CharacterCalculator characterCalculator) + { + List results = new LinkedList<>(); + SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(context); + List subscriptions = subscriptionManager.getActiveSubscriptionInfoList(); - private void setTransport(Type type) { - this.selectedType = type; + if (subscriptions.size() < 2) { + results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp, + context.getResources().getColor(R.color.grey_600), + text, composeHint, characterCalculator)); + } else { + for (SubscriptionInfoCompat subscriptionInfo : subscriptions) { + results.add(new TransportOption(Type.SMS, R.drawable.ic_send_sms_white_24dp, + context.getResources().getColor(R.color.grey_600), + text, composeHint, characterCalculator, + Optional.of(subscriptionInfo.getDisplayName()), + Optional.of(subscriptionInfo.getSubscriptionId()))); + } + } - notifyTransportChangeListeners(); + return results; } private void notifyTransportChangeListeners() { for (OnTransportChangedListener listener : listeners) { - listener.onChange(getSelectedTransport()); + listener.onChange(getSelectedTransport(), selectedOption.isPresent()); } } @@ -134,7 +180,15 @@ public class TransportOptions { return Optional.absent(); } + private boolean isEnabled(TransportOption transportOption) { + for (TransportOption option : enabledTransports) { + if (option.equals(transportOption)) return true; + } + + return false; + } + public interface OnTransportChangedListener { - public void onChange(TransportOption newTransport); + public void onChange(TransportOption newTransport, boolean manuallySelected); } } diff --git a/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java b/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java index 72d10a1f28..e301eb804f 100644 --- a/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java +++ b/src/org/thoughtcrime/securesms/TransportOptionsAdapter.java @@ -10,6 +10,8 @@ import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; +import org.thoughtcrime.securesms.util.ViewUtil; + import java.util.List; public class TransportOptionsAdapter extends BaseAdapter { @@ -51,14 +53,22 @@ public class TransportOptionsAdapter extends BaseAdapter { convertView = inflater.inflate(R.layout.transport_selection_list_item, parent, false); } - TransportOption transport = (TransportOption) getItem(position); - ImageView imageView = (ImageView) convertView.findViewById(R.id.icon); - TextView textView = (TextView) convertView.findViewById(R.id.text); + TransportOption transport = (TransportOption) getItem(position); + ImageView imageView = ViewUtil.findById(convertView, R.id.icon); + TextView textView = ViewUtil.findById(convertView, R.id.text); + TextView subtextView = ViewUtil.findById(convertView, R.id.subtext); imageView.getBackground().setColorFilter(transport.getBackgroundColor(), Mode.MULTIPLY); imageView.setImageResource(transport.getDrawable()); textView.setText(transport.getDescription()); + if (transport.getSimName().isPresent()) { + subtextView.setText(transport.getSimName().get()); + subtextView.setVisibility(View.VISIBLE); + } else { + subtextView.setVisibility(View.GONE); + } + return convertView; } } diff --git a/src/org/thoughtcrime/securesms/components/ComposeText.java b/src/org/thoughtcrime/securesms/components/ComposeText.java index ae9b85d175..4ae48d64d9 100644 --- a/src/org/thoughtcrime/securesms/components/ComposeText.java +++ b/src/org/thoughtcrime/securesms/components/ComposeText.java @@ -3,9 +3,11 @@ package org.thoughtcrime.securesms.components; import android.content.Context; import android.content.res.Configuration; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.InputType; import android.text.Spannable; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.text.style.RelativeSizeSpan; @@ -17,7 +19,9 @@ import org.thoughtcrime.securesms.components.emoji.EmojiEditText; import org.thoughtcrime.securesms.util.TextSecurePreferences; public class ComposeText extends EmojiEditText { + private SpannableString hint; + private SpannableString subHint; public ComposeText(Context context) { super(context); @@ -31,10 +35,18 @@ public class ComposeText extends EmojiEditText { super(context, attrs, defStyleAttr); } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); + if (!TextUtils.isEmpty(hint)) { - setHint(ellipsizeToWidth(hint)); + if (!TextUtils.isEmpty(subHint)) { + setHint(new SpannableStringBuilder().append(ellipsizeToWidth(hint)) + .append("\n") + .append(ellipsizeToWidth(subHint))); + } else { + setHint(ellipsizeToWidth(hint)); + } } } @@ -45,10 +57,24 @@ public class ComposeText extends EmojiEditText { TruncateAt.END); } - public void setHint(@NonNull String hint) { + public void setHint(@NonNull String hint, @Nullable CharSequence subHint) { this.hint = new SpannableString(hint); this.hint.setSpan(new RelativeSizeSpan(0.8f), 0, hint.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); - super.setHint(ellipsizeToWidth(this.hint)); + + if (subHint != null) { + this.subHint = new SpannableString(subHint); + this.subHint.setSpan(new RelativeSizeSpan(0.8f), 0, subHint.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } else { + this.subHint = null; + } + + if (this.subHint != null) { + super.setHint(new SpannableStringBuilder().append(ellipsizeToWidth(this.hint)) + .append("\n") + .append(ellipsizeToWidth(this.subHint))); + } else { + super.setHint(ellipsizeToWidth(this.hint)); + } } public void appendInvite(String invite) { @@ -88,6 +114,6 @@ public class ComposeText extends EmojiEditText { setInputType(inputType); setImeOptions(imeOptions); - setHint(transport.getComposeHint()); + setHint(transport.getComposeHint(), transport.getSimName().isPresent() ? "From " + transport.getSimName().get() : null); } } diff --git a/src/org/thoughtcrime/securesms/components/SendButton.java b/src/org/thoughtcrime/securesms/components/SendButton.java index e139ec18ae..f0e6d4e72e 100644 --- a/src/org/thoughtcrime/securesms/components/SendButton.java +++ b/src/org/thoughtcrime/securesms/components/SendButton.java @@ -83,14 +83,18 @@ public class SendButton extends ImageButton transportOptions.setDefaultTransport(type); } + public void setDefaultSubscriptionId(Optional subscriptionId) { + transportOptions.setDefaultSubscriptionId(subscriptionId); + } + @Override public void onSelected(TransportOption option) { - transportOptions.setSelectedTransport(option.getType()); + transportOptions.setSelectedTransport(option); getTransportOptionsPopup().dismiss(); } @Override - public void onChange(TransportOption newTransport) { + public void onChange(TransportOption newTransport, boolean isManualSelection) { setImageResource(newTransport.getDrawable()); setContentDescription(newTransport.getDescription()); } diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index d3ac714a47..8329150e4e 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -71,7 +71,8 @@ public class DatabaseFactory { private static final int INTRODUCED_ARCHIVE_VERSION = 24; private static final int INTRODUCED_CONVERSATION_LIST_STATUS_VERSION = 25; private static final int MIGRATED_CONVERSATION_LIST_STATUS_VERSION = 26; - private static final int DATABASE_VERSION = 26; + private static final int INTRODUCED_SUBSCRIPTION_ID_VERSION = 27; + private static final int DATABASE_VERSION = 27; private static final String DATABASE_NAME = "messages.db"; private static final Object lock = new Object(); @@ -813,6 +814,12 @@ public class DatabaseFactory { } } + if (oldVersion < INTRODUCED_SUBSCRIPTION_ID_VERSION) { + db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN default_subscription_id INTEGER DEFAULT -1"); + db.execSQL("ALTER TABLE sms ADD COLUMN subscription_id INTEGER DEFAULT -1"); + db.execSQL("ALTER TABLE mms ADD COLUMN subscription_id INTEGER DEFAULT -1"); + } + db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java b/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java index 0d8acec5c2..9109db04fa 100644 --- a/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/EncryptingSmsDatabase.java @@ -20,27 +20,24 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteOpenHelper; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.util.Log; import android.util.Pair; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher; import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret; +import org.thoughtcrime.securesms.crypto.MasterCipher; +import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; -import org.thoughtcrime.securesms.crypto.MasterSecretUtil; import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.util.LRUCache; import org.whispersystems.libaxolotl.InvalidMessageException; -import org.thoughtcrime.securesms.crypto.MasterCipher; -import org.thoughtcrime.securesms.crypto.MasterSecret; import java.lang.ref.SoftReference; import java.util.Collections; -import java.util.List; import java.util.Map; public class EncryptingSmsDatabase extends SmsDatabase { diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index 6b4a983fea..fd97a8f504 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -24,7 +24,6 @@ import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -110,7 +109,8 @@ public class MmsDatabase extends MessagingDatabase { "retr_txt" + " TEXT, " + "retr_txt_cs" + " INTEGER, " + "read_status" + " INTEGER, " + "ct_cls" + " INTEGER, " + "resp_txt" + " TEXT, " + "d_tm" + " INTEGER, " + RECEIPT_COUNT + " INTEGER DEFAULT 0, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + - NETWORK_FAILURE + " TEXT DEFAULT NULL," + "d_rpt" + " INTEGER);"; + NETWORK_FAILURE + " TEXT DEFAULT NULL," + "d_rpt" + " INTEGER, " + + SUBSCRIPTION_ID + " INTEGER DEFAULT -1);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", @@ -129,7 +129,7 @@ public class MmsDatabase extends MessagingDatabase { CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE, MESSAGE_SIZE, STATUS, TRANSACTION_ID, BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID, - RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, + RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID, AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS, AttachmentDatabase.UNIQUE_ID, AttachmentDatabase.MMS_ID, @@ -478,7 +478,7 @@ public class MmsDatabase extends MessagingDatabase { return new Pair<>(messageId, threadId); } - public Optional getNotification(long messageId) { + public Optional> getNotification(long messageId) { Cursor cursor = null; try { @@ -493,7 +493,8 @@ public class MmsDatabase extends MessagingDatabase { builder.addLong(MESSAGE_SIZE, PduHeaders.MESSAGE_SIZE); builder.addText(TRANSACTION_ID, PduHeaders.TRANSACTION_ID); - return Optional.of(new NotificationInd(headers)); + return Optional.of(new Pair<>(new NotificationInd(headers), + cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)))); } else { return Optional.absent(); } @@ -514,13 +515,14 @@ public class MmsDatabase extends MessagingDatabase { cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)}); if (cursor != null && cursor.moveToNext()) { - long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); - String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY)); - long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); - List attachments = new LinkedList(attachmentDatabase.getAttachmentsForMessage(messageId)); - MmsAddresses addresses = addr.getAddressesForId(messageId); - List destinations = new LinkedList<>(); - String body = getDecryptedBody(masterSecret, messageText, outboxType); + long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); + String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY)); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)); + List attachments = new LinkedList(attachmentDatabase.getAttachmentsForMessage(messageId)); + MmsAddresses addresses = addr.getAddressesForId(messageId); + List destinations = new LinkedList<>(); + String body = getDecryptedBody(masterSecret, messageText, outboxType); destinations.addAll(addresses.getBcc()); destinations.addAll(addresses.getCc()); @@ -532,7 +534,7 @@ public class MmsDatabase extends MessagingDatabase { return new OutgoingGroupMediaMessage(recipients, body, attachments, timestamp); } - OutgoingMediaMessage message = new OutgoingMediaMessage(recipients, body, attachments, timestamp, + OutgoingMediaMessage message = new OutgoingMediaMessage(recipients, body, attachments, timestamp, subscriptionId, !addresses.getBcc().isEmpty() ? ThreadDatabase.DistributionTypes.BROADCAST : ThreadDatabase.DistributionTypes.DEFAULT); if (Types.isSecureType(outboxType)) { @@ -615,6 +617,7 @@ public class MmsDatabase extends MessagingDatabase { contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); contentValues.put(DATE_RECEIVED, generatePduCompatTimestamp()); contentValues.put(PART_COUNT, retrieved.getAttachments().size()); + contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId()); contentValues.put(READ, 0); if (!contentValues.containsKey(DATE_SENT)) { @@ -673,7 +676,7 @@ public class MmsDatabase extends MessagingDatabase { return insertMessageInbox(masterSecret, retrieved, "", threadId, type); } - public Pair insertMessageInbox(@NonNull NotificationInd notification) { + public Pair insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context); long threadId = getThreadIdFor(notification); @@ -702,6 +705,7 @@ public class MmsDatabase extends MessagingDatabase { contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); contentValues.put(DATE_RECEIVED, generatePduCompatTimestamp()); contentValues.put(READ, Util.isDefaultSmsProvider(context) ? 0 : 1); + contentValues.put(SUBSCRIPTION_ID, subscriptionId); if (!contentValues.containsKey(DATE_SENT)) contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED)); @@ -761,6 +765,7 @@ public class MmsDatabase extends MessagingDatabase { contentValues.put(THREAD_ID, threadId); contentValues.put(READ, 1); contentValues.put(DATE_RECEIVED, System.currentTimeMillis()); + contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); if (message.getRecipients().isSingleRecipient()) { try { @@ -1021,6 +1026,7 @@ public class MmsDatabase extends MessagingDatabase { long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY)); int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS)); int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); byte[]contentLocationBytes = null; byte[]transactionIdBytes = null; @@ -1035,7 +1041,7 @@ public class MmsDatabase extends MessagingDatabase { return new NotificationMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), addressDeviceId, dateSent, dateReceived, receiptCount, threadId, contentLocationBytes, messageSize, expiry, status, - transactionIdBytes, mailbox); + transactionIdBytes, mailbox, subscriptionId); } private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { @@ -1051,6 +1057,7 @@ public class MmsDatabase extends MessagingDatabase { int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES)); String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID)); Recipients recipients = getRecipientsFor(address); List mismatches = getMismatchedIdentities(mismatchDocument); @@ -1059,7 +1066,8 @@ public class MmsDatabase extends MessagingDatabase { return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), addressDeviceId, dateSent, dateReceived, receiptCount, - threadId, body, slideDeck, partCount, box, mismatches, networkFailures); + threadId, body, slideDeck, partCount, box, mismatches, + networkFailures, subscriptionId); } private Recipients getRecipientsFor(String address) { diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java index cdeb24947c..0044948a93 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -14,6 +14,7 @@ public interface MmsSmsColumns { public static final String RECEIPT_COUNT = "delivery_receipt_count"; public static final String MISMATCHED_IDENTITIES = "mismatched_identities"; public static final String UNIQUE_ROW_ID = "unique_row_id"; + public static final String SUBSCRIPTION_ID = "subscription_id"; public static class Types { protected static final long TOTAL_MASK = 0xFFFFFFFF; diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index ca6d9e2389..2358e7a178 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -52,7 +52,8 @@ public class MmsSmsDatabase extends Database { MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT, + MmsDatabase.NETWORK_FAILURE, + MmsSmsColumns.SUBSCRIPTION_ID, TRANSPORT, AttachmentDatabase.ATTACHMENT_ID_ALIAS, AttachmentDatabase.UNIQUE_ID, AttachmentDatabase.MMS_ID, @@ -132,7 +133,7 @@ public class MmsSmsDatabase extends Database { MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES, - MmsDatabase.NETWORK_FAILURE, TRANSPORT, + MmsSmsColumns.SUBSCRIPTION_ID, MmsDatabase.NETWORK_FAILURE, TRANSPORT, AttachmentDatabase.UNIQUE_ID, AttachmentDatabase.MMS_ID, AttachmentDatabase.SIZE, @@ -156,6 +157,7 @@ public class MmsSmsDatabase extends Database { MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID, MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES, + MmsSmsColumns.SUBSCRIPTION_ID, MmsDatabase.NETWORK_FAILURE, TRANSPORT, AttachmentDatabase.UNIQUE_ID, AttachmentDatabase.MMS_ID, @@ -192,6 +194,7 @@ public class MmsSmsDatabase extends Database { mmsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID); mmsColumnsPresent.add(MmsSmsColumns.RECEIPT_COUNT); mmsColumnsPresent.add(MmsSmsColumns.MISMATCHED_IDENTITIES); + mmsColumnsPresent.add(MmsSmsColumns.SUBSCRIPTION_ID); mmsColumnsPresent.add(MmsDatabase.MESSAGE_TYPE); mmsColumnsPresent.add(MmsDatabase.MESSAGE_BOX); mmsColumnsPresent.add(MmsDatabase.DATE_SENT); @@ -222,6 +225,7 @@ public class MmsSmsDatabase extends Database { smsColumnsPresent.add(MmsSmsColumns.THREAD_ID); smsColumnsPresent.add(MmsSmsColumns.RECEIPT_COUNT); smsColumnsPresent.add(MmsSmsColumns.MISMATCHED_IDENTITIES); + smsColumnsPresent.add(MmsSmsColumns.SUBSCRIPTION_ID); smsColumnsPresent.add(SmsDatabase.TYPE); smsColumnsPresent.add(SmsDatabase.SUBJECT); smsColumnsPresent.add(SmsDatabase.DATE_SENT); diff --git a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java index 02f21f0f37..7d1271acb6 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java @@ -23,15 +23,16 @@ public class RecipientPreferenceDatabase extends Database { private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName(); private static final String RECIPIENT_PREFERENCES_URI = "content://textsecure/recipients/"; - private static final String TABLE_NAME = "recipient_preferences"; - private static final String ID = "_id"; - private static final String RECIPIENT_IDS = "recipient_ids"; - private static final String BLOCK = "block"; - private static final String NOTIFICATION = "notification"; - private static final String VIBRATE = "vibrate"; - private static final String MUTE_UNTIL = "mute_until"; - private static final String COLOR = "color"; - private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; + private static final String TABLE_NAME = "recipient_preferences"; + private static final String ID = "_id"; + private static final String RECIPIENT_IDS = "recipient_ids"; + private static final String BLOCK = "block"; + private static final String NOTIFICATION = "notification"; + private static final String VIBRATE = "vibrate"; + private static final String MUTE_UNTIL = "mute_until"; + private static final String COLOR = "color"; + private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder"; + private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id"; public enum VibrateState { DEFAULT(0), ENABLED(1), DISABLED(2); @@ -60,7 +61,8 @@ public class RecipientPreferenceDatabase extends Database { VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + MUTE_UNTIL + " INTEGER DEFAULT 0, " + COLOR + " TEXT DEFAULT NULL, " + - SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0);"; + SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0, " + + DEFAULT_SUBSCRIPTION_ID + " INTEGER DEFAULT -1);"; public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) { super(context, databaseHelper); @@ -88,13 +90,14 @@ public class RecipientPreferenceDatabase extends Database { null, null, null); if (cursor != null && cursor.moveToNext()) { - boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; - String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); - int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); - long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL)); - String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR)); - Uri notificationUri = notification == null ? null : Uri.parse(notification); - boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1; + boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; + String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); + int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); + long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL)); + String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR)); + Uri notificationUri = notification == null ? null : Uri.parse(notification); + boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1; + int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID)); MaterialColor color; @@ -109,7 +112,8 @@ public class RecipientPreferenceDatabase extends Database { return Optional.of(new RecipientsPreferences(blocked, muteUntil, VibrateState.fromId(vibrateState), - notificationUri, color, seenInviteReminder)); + notificationUri, color, seenInviteReminder, + defaultSubscriptionId)); } return Optional.absent(); @@ -124,6 +128,13 @@ public class RecipientPreferenceDatabase extends Database { updateOrInsert(recipients, values); } + public void setDefaultSubscriptionId(@NonNull Recipients recipients, int defaultSubscriptionId) { + ContentValues values = new ContentValues(); + values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId); + updateOrInsert(recipients, values); + } + + public void setBlocked(Recipients recipients, boolean blocked) { ContentValues values = new ContentValues(); values.put(BLOCK, blocked ? 1 : 0); @@ -181,19 +192,22 @@ public class RecipientPreferenceDatabase extends Database { private final Uri notification; private final MaterialColor color; private final boolean seenInviteReminder; + private final int defaultSubscriptionId; public RecipientsPreferences(boolean blocked, long muteUntil, @NonNull VibrateState vibrateState, @Nullable Uri notification, @Nullable MaterialColor color, - boolean seenInviteReminder) + boolean seenInviteReminder, + int defaultSubscriptionId) { - this.blocked = blocked; - this.muteUntil = muteUntil; - this.vibrateState = vibrateState; - this.notification = notification; - this.color = color; - this.seenInviteReminder = seenInviteReminder; + this.blocked = blocked; + this.muteUntil = muteUntil; + this.vibrateState = vibrateState; + this.notification = notification; + this.color = color; + this.seenInviteReminder = seenInviteReminder; + this.defaultSubscriptionId = defaultSubscriptionId; } public @Nullable MaterialColor getColor() { @@ -219,5 +233,9 @@ public class RecipientPreferenceDatabase extends Database { public boolean hasSeenInviteReminder() { return seenInviteReminder; } + + public Optional getDefaultSubscriptionId() { + return defaultSubscriptionId != -1 ? Optional.of(defaultSubscriptionId) : Optional.absent(); + } } } diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java index 8a6f8c9fd8..771ca39620 100644 --- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -41,7 +41,6 @@ import org.thoughtcrime.securesms.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.LRUCache; import org.whispersystems.jobqueue.JobManager; import org.whispersystems.textsecure.api.util.InvalidNumberException; @@ -78,7 +77,7 @@ public class SmsDatabase extends MessagingDatabase { DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " + RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " + - MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT);"; + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", @@ -95,7 +94,7 @@ public class SmsDatabase extends MessagingDatabase { DATE_SENT + " AS " + NORMALIZED_DATE_SENT, PROTOCOL, READ, STATUS, TYPE, REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, RECEIPT_COUNT, - MISMATCHED_IDENTITIES + MISMATCHED_IDENTITIES, SUBSCRIPTION_ID }; private static final EarlyReceiptCache earlyReceiptCache = new EarlyReceiptCache(); @@ -451,6 +450,7 @@ public class SmsDatabase extends MessagingDatabase { values.put(DATE_SENT, message.getSentTimestampMillis()); values.put(PROTOCOL, message.getProtocol()); values.put(READ, unread ? 0 : 1); + values.put(SUBSCRIPTION_ID, message.getSubscriptionId()); if (!TextUtils.isEmpty(message.getPseudoSubject())) values.put(SUBJECT, message.getPseudoSubject()); @@ -497,6 +497,7 @@ public class SmsDatabase extends MessagingDatabase { contentValues.put(DATE_SENT, date); contentValues.put(READ, 1); contentValues.put(TYPE, type); + contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId()); try { contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(date, canonicalizeNumber(context, address))); @@ -663,6 +664,7 @@ public class SmsDatabase extends MessagingDatabase { int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.RECEIPT_COUNT)); String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES)); + int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID)); List mismatches = getMismatches(mismatchDocument); Recipients recipients = getRecipientsFor(address); @@ -672,7 +674,7 @@ public class SmsDatabase extends MessagingDatabase { recipients.getPrimaryRecipient(), addressDeviceId, dateSent, dateReceived, receiptCount, type, - threadId, status, mismatches); + threadId, status, mismatches, subscriptionId); } private Recipients getRecipientsFor(String address) { diff --git a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java index deb32c37b7..7615e7a021 100644 --- a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java @@ -21,8 +21,8 @@ import android.support.annotation.NonNull; import android.text.SpannableString; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.database.SmsDatabase.Status; import org.thoughtcrime.securesms.database.MmsDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase.Status; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.mms.SlideDeck; @@ -53,10 +53,10 @@ public class MediaMmsMessageRecord extends MessageRecord { @NonNull SlideDeck slideDeck, int partCount, long mailbox, List mismatches, - List failures) + List failures, int subscriptionId) { super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent, - dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures); + dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures, subscriptionId); this.context = context.getApplicationContext(); this.partCount = partCount; diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java index dcba1fefbd..608e14304e 100644 --- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -50,13 +50,15 @@ public abstract class MessageRecord extends DisplayRecord { private final long id; private final List mismatches; private final List networkFailures; + private final int subscriptionId; MessageRecord(Context context, long id, Body body, Recipients recipients, Recipient individualRecipient, int recipientDeviceId, long dateSent, long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type, List mismatches, - List networkFailures) + List networkFailures, + int subscriptionId) { super(context, body, recipients, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, type); @@ -65,6 +67,7 @@ public abstract class MessageRecord extends DisplayRecord { this.recipientDeviceId = recipientDeviceId; this.mismatches = mismatches; this.networkFailures = networkFailures; + this.subscriptionId = subscriptionId; } public abstract boolean isMms(); @@ -195,4 +198,7 @@ public abstract class MessageRecord extends DisplayRecord { return (int)getId(); } + public int getSubscriptionId() { + return subscriptionId; + } } diff --git a/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java index 2dfa01547a..8da31f5308 100644 --- a/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java @@ -49,11 +49,12 @@ public class NotificationMmsMessageRecord extends MessageRecord { Recipient individualRecipient, int recipientDeviceId, long dateSent, long dateReceived, int receiptCount, long threadId, byte[] contentLocation, long messageSize, - long expiry, int status, byte[] transactionId, long mailbox) + long expiry, int status, byte[] transactionId, long mailbox, + int subscriptionId) { super(context, id, new Body("", true), recipients, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, - new LinkedList(), new LinkedList()); + new LinkedList(), new LinkedList(), subscriptionId); this.contentLocation = contentLocation; this.messageSize = messageSize; diff --git a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java index cdbd38d894..6000aea9db 100644 --- a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java +++ b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java @@ -47,11 +47,12 @@ public class SmsMessageRecord extends MessageRecord { long dateSent, long dateReceived, int receiptCount, long type, long threadId, - int status, List mismatches) + int status, List mismatches, + int subscriptionId) { super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, status, receiptCount, type, - mismatches, new LinkedList()); + mismatches, new LinkedList(), subscriptionId); } public long getType() { diff --git a/src/org/thoughtcrime/securesms/groups/GroupManager.java b/src/org/thoughtcrime/securesms/groups/GroupManager.java index c98c6c18b4..adc2f4da57 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/src/org/thoughtcrime/securesms/groups/GroupManager.java @@ -107,8 +107,8 @@ public class GroupManager { avatarAttachment = new UriAttachment(avatarUri, ContentType.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length); } - OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis()); - long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false); + OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis()); + long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false); return new GroupActionResult(groupRecipient, threadId); } diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 0c4765a9aa..260277d17d 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -9,9 +9,9 @@ import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecretUnion; +import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.ApnUnavailableException; import org.thoughtcrime.securesms.mms.CompatMmsConnection; @@ -35,7 +35,6 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; -import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.EncodedStringValue; import ws.com.google.android.mms.pdu.NotificationInd; @@ -75,8 +74,8 @@ public class MmsDownloadJob extends MasterSecretJob { @Override public void onRun(MasterSecret masterSecret) { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Optional notification = database.getNotification(messageId); + MmsDatabase database = DatabaseFactory.getMmsDatabase(context); + Optional> notification = database.getNotification(messageId); if (!notification.isPresent()) { Log.w(TAG, "No notification for ID: " + messageId); @@ -84,22 +83,24 @@ public class MmsDownloadJob extends MasterSecretJob { } try { - if (notification.get().getContentLocation() == null) { + if (notification.get().first.getContentLocation() == null) { throw new MmsException("Notification content location was null."); } database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING); - String contentLocation = new String(notification.get().getContentLocation()); - byte[] transactionId = notification.get().getTransactionId(); + String contentLocation = new String(notification.get().first.getContentLocation()); + byte[] transactionId = notification.get().first.getTransactionId(); Log.w(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost()); - RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId); + RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().second); + if (retrieveConf == null) { throw new MmsException("RetrieveConf was null"); } - storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieveConf); + + storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieveConf, notification.get().second); } catch (ApnUnavailableException e) { Log.w(TAG, e); handleDownloadError(masterSecret, messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE, @@ -146,7 +147,8 @@ public class MmsDownloadJob extends MasterSecretJob { } private void storeRetrievedMms(MasterSecret masterSecret, String contentLocation, - long messageId, long threadId, RetrieveConf retrieved) + long messageId, long threadId, RetrieveConf retrieved, + int subscriptionId) throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException, LegacyMessageException { @@ -192,7 +194,7 @@ public class MmsDownloadJob extends MasterSecretJob { - IncomingMediaMessage message = new IncomingMediaMessage(from, to, cc, body, retrieved.getDate() * 1000L, attachments); + IncomingMediaMessage message = new IncomingMediaMessage(from, to, cc, body, retrieved.getDate() * 1000L, attachments, subscriptionId); Pair messageAndThreadId = database.insertMessageInbox(new MasterSecretUnion(masterSecret), message, contentLocation, threadId); diff --git a/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java index 6ae212cb99..7bb413f6e2 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsReceiveJob.java @@ -19,16 +19,20 @@ import ws.com.google.android.mms.pdu.PduParser; public class MmsReceiveJob extends ContextJob { + private static final long serialVersionUID = 1L; + private static final String TAG = MmsReceiveJob.class.getSimpleName(); private final byte[] data; + private final int subscriptionId; - public MmsReceiveJob(Context context, byte[] data) { + public MmsReceiveJob(Context context, byte[] data, int subscriptionId) { super(context, JobParameters.newBuilder() .withWakeLock(true) .withPersistence().create()); - this.data = data; + this.data = data; + this.subscriptionId = subscriptionId; } @Override @@ -54,7 +58,7 @@ public class MmsReceiveJob extends ContextJob { if (isNotification(pdu) && !isBlocked(pdu)) { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - Pair messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu); + Pair messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu, subscriptionId); Log.w(TAG, "Inserted received MMS notification..."); diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index 4d3d71c9b9..796b953f8f 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -17,7 +17,6 @@ import org.thoughtcrime.securesms.mms.MmsSendResult; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.notifications.MessageNotifier; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; @@ -25,12 +24,9 @@ import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.SmilUtil; import org.thoughtcrime.securesms.util.TelephonyUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; -import org.whispersystems.textsecure.api.util.InvalidNumberException; -import org.whispersystems.textsecure.api.util.PhoneNumberFormatter; import java.io.IOException; import java.util.Arrays; @@ -83,7 +79,7 @@ public class MmsSendJob extends SendJob { validateDestinations(message, pdu); final byte[] pduBytes = getPduBytes(pdu); - final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes); + final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes, message.getSubscriptionId()); final MmsSendResult result = getSendResult(sendConf, pdu); database.markAsSent(messageId); diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 33c3c5012f..bbbc9a46bb 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -261,7 +261,7 @@ public class PushDecryptJob extends ContextJob { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); String localNumber = TextSecurePreferences.getLocalNumber(context); IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, envelope.getSource(), - localNumber, message.getTimestamp(), + localNumber, message.getTimestamp(), -1, Optional.fromNullable(envelope.getRelay()), message.getBody(), message.getGroupInfo(), @@ -293,7 +293,7 @@ public class PushDecryptJob extends ContextJob { Recipients recipients = getSyncMessageDestination(message); OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(), PointerAttachment.forPointers(masterSecret, message.getMessage().getAttachments()), - message.getTimestamp(), ThreadDatabase.DistributionTypes.DEFAULT); + message.getTimestamp(), -1, ThreadDatabase.DistributionTypes.DEFAULT); mediaMessage = new OutgoingSecureMediaMessage(mediaMessage); @@ -350,7 +350,7 @@ public class PushDecryptJob extends ContextJob { EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); Recipients recipients = getSyncMessageDestination(message); String body = message.getMessage().getBody().or(""); - OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipients, body); + OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipients, body, -1); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingTextMessage, false, message.getTimestamp()); diff --git a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java index 23c3f874db..a829c56eed 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsReceiveJob.java @@ -23,17 +23,21 @@ import java.util.List; public class SmsReceiveJob extends ContextJob { + private static final long serialVersionUID = 1L; + private static final String TAG = SmsReceiveJob.class.getSimpleName(); private final Object[] pdus; + private final int subscriptionId; - public SmsReceiveJob(Context context, Object[] pdus) { + public SmsReceiveJob(Context context, Object[] pdus, int subscriptionId) { super(context, JobParameters.newBuilder() .withPersistence() .withWakeLock(true) .create()); - this.pdus = pdus; + this.pdus = pdus; + this.subscriptionId = subscriptionId; } @Override @@ -41,7 +45,7 @@ public class SmsReceiveJob extends ContextJob { @Override public void onRun() { - Optional message = assembleMessageFragments(pdus); + Optional message = assembleMessageFragments(pdus, subscriptionId); MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); MasterSecretUnion masterSecretUnion; @@ -95,11 +99,11 @@ public class SmsReceiveJob extends ContextJob { return messageAndThreadId; } - private Optional assembleMessageFragments(Object[] pdus) { + private Optional assembleMessageFragments(Object[] pdus, int subscriptionId) { List messages = new LinkedList<>(); for (Object pdu : pdus) { - messages.add(new IncomingTextMessage(SmsMessage.createFromPdu((byte[])pdu))); + messages.add(new IncomingTextMessage(SmsMessage.createFromPdu((byte[])pdu), subscriptionId)); } if (messages.isEmpty()) { diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java index f93c42b266..e6b916f015 100644 --- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java @@ -4,6 +4,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.telephony.PhoneNumberUtils; import android.telephony.SmsManager; import android.util.Log; @@ -105,7 +106,7 @@ public class SmsSendJob extends SendJob { // catching it and marking the message as a failure. That way at least it doesn't // repeatedly crash every time you start the app. try { - SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents); + getSmsManagerFor(message.getSubscriptionId()).sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents); } catch (NullPointerException npe) { Log.w(TAG, npe); Log.w(TAG, "Recipient: " + recipient); @@ -113,9 +114,9 @@ public class SmsSendJob extends SendJob { try { for (int i=0;i= 22 && subscriptionId != -1) { + return SmsManager.getSmsManagerForSubscriptionId(subscriptionId); + } else { + return SmsManager.getDefault(); + } + } + private static JobParameters constructParameters(Context context, String name) { JobParameters.Builder builder = JobParameters.newBuilder() .withPersistence() diff --git a/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java b/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java index c93415f751..b80ba0241d 100644 --- a/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; +import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.support.annotation.NonNull; @@ -26,38 +27,46 @@ public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsCo @Nullable @Override - public SendConf send(@NonNull byte[] pduBytes) + public SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException { - try { + if (subscriptionId == -1 || VERSION.SDK_INT < 22) { Log.w(TAG, "Sending via legacy connection"); - return new OutgoingLegacyMmsConnection(context).send(pduBytes); - } catch (UndeliverableMessageException | ApnUnavailableException e) { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - Log.w(TAG, "Falling back to try sending via Lollipop API"); - return new OutgoingLollipopMmsConnection(context).send(pduBytes); - } else { - throw new UndeliverableMessageException(e); + try { + return new OutgoingLegacyMmsConnection(context).send(pduBytes, subscriptionId); + } catch (UndeliverableMessageException | ApnUnavailableException e) { + Log.w(TAG, e); } } + + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId); + } else { + throw new UndeliverableMessageException("Lollipop API not available to try..."); + } } @Nullable @Override public RetrieveConf retrieve(@NonNull String contentLocation, - byte[] transactionId) + byte[] transactionId, + int subscriptionId) throws MmsException, MmsRadioException, ApnUnavailableException, IOException { - try { + if (VERSION.SDK_INT < 22 || subscriptionId == -1) { Log.w(TAG, "Receiving via legacy connection"); - return new IncomingLegacyMmsConnection(context).retrieve(contentLocation, transactionId); - } catch (MmsRadioException | IOException | ApnUnavailableException e) { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - Log.w(TAG, "Falling back to try receiving via Lollipop API"); - return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId); - } else { - throw e; + try { + return new IncomingLegacyMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId); + } catch (MmsRadioException | ApnUnavailableException | IOException e) { + Log.w(TAG, e); } } + + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + Log.w(TAG, "Falling back to try receiving via Lollipop API"); + return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId); + } else { + throw new IOException("Not able to use Lollipop APIs, giving up..."); + } } } diff --git a/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java index 547a711cd8..e4ccdb86f9 100644 --- a/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java @@ -60,7 +60,7 @@ public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements @Override public @Nullable RetrieveConf retrieve(@NonNull String contentLocation, - byte[] transactionId) + byte[] transactionId, int subscriptionId) throws MmsRadioException, ApnUnavailableException, IOException { MmsRadio radio = MmsRadio.getInstance(context); diff --git a/src/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java b/src/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java index 7305120e4b..984c0b2f35 100644 --- a/src/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/IncomingLollipopMmsConnection.java @@ -39,6 +39,7 @@ import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.RetrieveConf; public class IncomingLollipopMmsConnection extends LollipopMmsConnection implements IncomingMmsConnection { + public static final String ACTION = IncomingLollipopMmsConnection.class.getCanonicalName() + "MMS_DOWNLOADED_ACTION"; private static final String TAG = IncomingLollipopMmsConnection.class.getSimpleName(); @@ -58,7 +59,8 @@ public class IncomingLollipopMmsConnection extends LollipopMmsConnection impleme @Override @TargetApi(VERSION_CODES.LOLLIPOP) public synchronized @Nullable RetrieveConf retrieve(@NonNull String contentLocation, - byte[] transactionId) throws MmsException + byte[] transactionId, + int subscriptionId) throws MmsException { beginTransaction(); @@ -66,11 +68,20 @@ public class IncomingLollipopMmsConnection extends LollipopMmsConnection impleme MmsBodyProvider.Pointer pointer = MmsBodyProvider.makeTemporaryPointer(getContext()); Log.w(TAG, "downloading multimedia from " + contentLocation + " to " + pointer.getUri()); - SmsManager.getDefault().downloadMultimediaMessage(getContext(), - contentLocation, - pointer.getUri(), - null, - getPendingIntent()); + + SmsManager smsManager; + + if (VERSION.SDK_INT >= 22 && subscriptionId != -1) { + smsManager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId); + } else { + smsManager = SmsManager.getDefault(); + } + + smsManager.downloadMultimediaMessage(getContext(), + contentLocation, + pointer.getUri(), + null, + getPendingIntent()); waitForResult(); diff --git a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java index b9b384d109..b47c796e1d 100644 --- a/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java +++ b/src/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java @@ -19,6 +19,7 @@ public class IncomingMediaMessage { private final String groupId; private final boolean push; private final long sentTimeMillis; + private final int subscriptionId; private final List to = new LinkedList<>(); private final List cc = new LinkedList<>(); @@ -26,13 +27,14 @@ public class IncomingMediaMessage { public IncomingMediaMessage(String from, List to, List cc, String body, long sentTimeMillis, - List attachments) + List attachments, int subscriptionId) { this.from = from; this.sentTimeMillis = sentTimeMillis; this.body = body; this.groupId = null; this.push = false; + this.subscriptionId = subscriptionId; this.to.addAll(to); this.cc.addAll(cc); @@ -43,6 +45,7 @@ public class IncomingMediaMessage { String from, String to, long sentTimeMillis, + int subscriptionId, Optional relay, Optional body, Optional group, @@ -52,6 +55,7 @@ public class IncomingMediaMessage { this.from = from; this.sentTimeMillis = sentTimeMillis; this.body = body.orNull(); + this.subscriptionId = subscriptionId; if (group.isPresent()) this.groupId = GroupUtil.getEncodedId(group.get().getGroupId()); else this.groupId = null; @@ -60,6 +64,10 @@ public class IncomingMediaMessage { this.attachments.addAll(PointerAttachment.forPointers(masterSecret, attachments)); } + public int getSubscriptionId() { + return subscriptionId; + } + public String getBody() { return body; } diff --git a/src/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java b/src/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java index 0b42fcac18..d29724511b 100644 --- a/src/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/IncomingMmsConnection.java @@ -9,5 +9,5 @@ import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.pdu.RetrieveConf; public interface IncomingMmsConnection { - @Nullable RetrieveConf retrieve(@NonNull String contentLocation, byte[] transactionId) throws MmsException, MmsRadioException, ApnUnavailableException, IOException; + @Nullable RetrieveConf retrieve(@NonNull String contentLocation, byte[] transactionId, int subscriptionId) throws MmsException, MmsRadioException, ApnUnavailableException, IOException; } diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java index eda1836235..2ac7c241ab 100644 --- a/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/OutgoingLegacyMmsConnection.java @@ -72,7 +72,7 @@ public class OutgoingLegacyMmsConnection extends LegacyMmsConnection implements } @Override - public @Nullable SendConf send(@NonNull byte[] pduBytes) throws UndeliverableMessageException { + public @Nullable SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException { try { MmsRadio radio = MmsRadio.getInstance(context); diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java b/src/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java index 20323247e0..d6c2e0740f 100644 --- a/src/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/OutgoingLollipopMmsConnection.java @@ -59,17 +59,27 @@ public class OutgoingLollipopMmsConnection extends LollipopMmsConnection impleme @Override @TargetApi(VERSION_CODES.LOLLIPOP) - public @Nullable synchronized SendConf send(@NonNull byte[] pduBytes) throws UndeliverableMessageException { + public @Nullable synchronized SendConf send(@NonNull byte[] pduBytes, int subscriptionId) + throws UndeliverableMessageException + { beginTransaction(); try { MmsBodyProvider.Pointer pointer = MmsBodyProvider.makeTemporaryPointer(getContext()); Util.copy(new ByteArrayInputStream(pduBytes), pointer.getOutputStream()); - SmsManager.getDefault().sendMultimediaMessage(getContext(), - pointer.getUri(), - null, - null, - getPendingIntent()); + SmsManager smsManager; + + if (VERSION.SDK_INT >= 22 && subscriptionId != -1) { + smsManager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId); + } else { + smsManager = SmsManager.getDefault(); + } + + smsManager.sendMultimediaMessage(getContext(), + pointer.getUri(), + null, + null, + getPendingIntent()); waitForResult(); diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java b/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java index c47ef5663e..93f5c23a47 100644 --- a/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java +++ b/src/org/thoughtcrime/securesms/mms/OutgoingMediaMessage.java @@ -14,9 +14,11 @@ public class OutgoingMediaMessage { protected final List attachments; private final long sentTimeMillis; private final int distributionType; + private final int subscriptionId; public OutgoingMediaMessage(Recipients recipients, String message, List attachments, long sentTimeMillis, + int subscriptionId, int distributionType) { this.recipients = recipients; @@ -24,14 +26,15 @@ public class OutgoingMediaMessage { this.sentTimeMillis = sentTimeMillis; this.distributionType = distributionType; this.attachments = attachments; + this.subscriptionId = subscriptionId; } - public OutgoingMediaMessage(Recipients recipients, SlideDeck slideDeck, String message, long sentTimeMillis, int distributionType) + public OutgoingMediaMessage(Recipients recipients, SlideDeck slideDeck, String message, long sentTimeMillis, int subscriptionId, int distributionType) { this(recipients, buildMessage(slideDeck, message), slideDeck.asAttachments(), - sentTimeMillis, + sentTimeMillis, subscriptionId, distributionType); } @@ -41,6 +44,7 @@ public class OutgoingMediaMessage { this.distributionType = that.distributionType; this.attachments = that.attachments; this.sentTimeMillis = that.sentTimeMillis; + this.subscriptionId = that.subscriptionId; } public Recipients getRecipients() { @@ -71,6 +75,10 @@ public class OutgoingMediaMessage { return sentTimeMillis; } + public int getSubscriptionId() { + return subscriptionId; + } + private static String buildMessage(SlideDeck slideDeck, String message) { if (!TextUtils.isEmpty(message) && !TextUtils.isEmpty(slideDeck.getBody())) { return slideDeck.getBody() + "\n\n" + message; diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java b/src/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java index 5fc669456e..64a3c58a99 100644 --- a/src/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/OutgoingMmsConnection.java @@ -8,5 +8,5 @@ import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import ws.com.google.android.mms.pdu.SendConf; public interface OutgoingMmsConnection { - @Nullable SendConf send(@NonNull byte[] pduBytes) throws UndeliverableMessageException; + @Nullable SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException; } diff --git a/src/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java b/src/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java index 084579db62..ae20107388 100644 --- a/src/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java +++ b/src/org/thoughtcrime/securesms/mms/OutgoingSecureMediaMessage.java @@ -16,7 +16,7 @@ public class OutgoingSecureMediaMessage extends OutgoingMediaMessage { long sentTimeMillis, int distributionType) { - super(recipients, body, attachments, sentTimeMillis, distributionType); + super(recipients, body, attachments, sentTimeMillis, -1, distributionType); } public OutgoingSecureMediaMessage(OutgoingMediaMessage base) { diff --git a/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java index 5ca13526d4..a352dd70dc 100644 --- a/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java @@ -27,16 +27,16 @@ import android.support.v4.app.RemoteInput; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.whispersystems.libaxolotl.util.guava.Optional; import java.util.LinkedList; -import ws.com.google.android.mms.pdu.PduBody; - /** * Get the response text from the Wearable Device and sends an message as a reply */ @@ -66,11 +66,14 @@ public class WearReplyReceiver extends MasterSecretBroadcastReceiver { protected Void doInBackground(Void... params) { long threadId; + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipientIds); + int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; + if (recipients.isGroupRecipient()) { - OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList(), System.currentTimeMillis(), 0); + OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList(), System.currentTimeMillis(), subscriptionId, 0); threadId = MessageSender.send(context, masterSecret, reply, -1, false); } else { - OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString()); + OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), subscriptionId); threadId = MessageSender.send(context, masterSecret, reply, -1, false); } diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java index d2543bdca7..8c91ba8539 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java @@ -66,7 +66,7 @@ public class RecipientFactory { return provider.getRecipients(context, recipientIds, asynchronous); } - public static Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) { + public static @NonNull Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) { StringTokenizer tokenizer = new StringTokenizer(rawText, ","); List ids = new LinkedList<>(); @@ -81,7 +81,7 @@ public class RecipientFactory { return getRecipientsForIds(context, ids, asynchronous); } - public static Recipients getRecipientsFromStrings(@NonNull Context context, @NonNull List numbers, boolean asynchronous) { + public static @NonNull Recipients getRecipientsFromStrings(@NonNull Context context, @NonNull List numbers, boolean asynchronous) { List ids = new LinkedList<>(); for (String number : numbers) { @@ -95,7 +95,7 @@ public class RecipientFactory { return getRecipientsForIds(context, ids, asynchronous); } - private static Recipients getRecipientsForIds(Context context, List idStrings, boolean asynchronous) { + private static @NonNull Recipients getRecipientsForIds(Context context, List idStrings, boolean asynchronous) { long[] ids = new long[idStrings.size()]; int i = 0; diff --git a/src/org/thoughtcrime/securesms/service/MmsListener.java b/src/org/thoughtcrime/securesms/service/MmsListener.java index 02d0d27d20..3d8417d809 100644 --- a/src/org/thoughtcrime/securesms/service/MmsListener.java +++ b/src/org/thoughtcrime/securesms/service/MmsListener.java @@ -63,9 +63,11 @@ public class MmsListener extends BroadcastReceiver { isRelevant(context, intent))) { Log.w(TAG, "Relevant!"); + int subscriptionId = intent.getExtras().getInt("subscription", -1); + ApplicationContext.getInstance(context) .getJobManager() - .add(new MmsReceiveJob(context, intent.getByteArrayExtra("data"))); + .add(new MmsReceiveJob(context, intent.getByteArrayExtra("data"), subscriptionId)); abortBroadcast(); } diff --git a/src/org/thoughtcrime/securesms/service/QuickResponseService.java b/src/org/thoughtcrime/securesms/service/QuickResponseService.java index 77e544ccac..467f4d0e98 100644 --- a/src/org/thoughtcrime/securesms/service/QuickResponseService.java +++ b/src/org/thoughtcrime/securesms/service/QuickResponseService.java @@ -9,6 +9,8 @@ import android.widget.Toast; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.SlideDeck; @@ -17,6 +19,7 @@ import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.util.Rfc5724Uri; +import org.whispersystems.libaxolotl.util.guava.Optional; import java.net.URISyntaxException; import java.net.URLDecoder; @@ -49,14 +52,17 @@ public class QuickResponseService extends MasterSecretIntentService { if(numbers.contains("%")){ numbers = URLDecoder.decode(numbers); } - Recipients recipients = RecipientFactory.getRecipientsFromString(this, numbers, false); + + Recipients recipients = RecipientFactory.getRecipientsFromString(this, numbers, false); + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(this).getRecipientsPreferences(recipients.getIds()); + int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1; if (!TextUtils.isEmpty(content)) { if (recipients.isSingleRecipient()) { - MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipients, content), -1, false); + MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipients, content, subscriptionId), -1, false); } else { MessageSender.send(this, masterSecret, new OutgoingMediaMessage(recipients, new SlideDeck(), content, System.currentTimeMillis(), - ThreadDatabase.DistributionTypes.DEFAULT), -1, false); + subscriptionId, ThreadDatabase.DistributionTypes.DEFAULT), -1, false); } } } catch (URISyntaxException e) { diff --git a/src/org/thoughtcrime/securesms/service/SmsListener.java b/src/org/thoughtcrime/securesms/service/SmsListener.java index 8a58d90a04..82395bbd65 100644 --- a/src/org/thoughtcrime/securesms/service/SmsListener.java +++ b/src/org/thoughtcrime/securesms/service/SmsListener.java @@ -84,16 +84,6 @@ public class SmsListener extends BroadcastReceiver { return bodyBuilder.toString(); } -// private ArrayList getAsTextMessages(Intent intent) { -// Object[] pdus = (Object[])intent.getExtras().get("pdus"); -// ArrayList messages = new ArrayList(pdus.length); -// -// for (int i=0;i fragments) { @@ -112,6 +117,7 @@ public class IncomingTextMessage implements Parcelable { this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis(); this.groupId = fragments.get(0).getGroupId(); this.push = fragments.get(0).isPush(); + this.subscriptionId = fragments.get(0).getSubscriptionId(); } protected IncomingTextMessage(String sender, String groupId) @@ -126,6 +132,11 @@ public class IncomingTextMessage implements Parcelable { this.sentTimestampMillis = System.currentTimeMillis(); this.groupId = groupId; this.push = true; + this.subscriptionId = -1; + } + + public int getSubscriptionId() { + return subscriptionId; } public long getSentTimestampMillis() { @@ -209,5 +220,6 @@ public class IncomingTextMessage implements Parcelable { out.writeLong(sentTimestampMillis); out.writeString(groupId); out.writeInt(push ? 1 : 0); + out.writeInt(subscriptionId); } } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 8e94d583c1..28f59b0fe8 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -74,7 +74,8 @@ public class MessageSender { allocatedThreadId = threadId; } - long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId, message, forceSms, System.currentTimeMillis()); + long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId, + message, forceSms, System.currentTimeMillis()); sendTextMessage(context, recipients, forceSms, keyExchange, messageId); diff --git a/src/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java b/src/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java index 4025157a85..dfc0c66002 100644 --- a/src/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java +++ b/src/org/thoughtcrime/securesms/sms/OutgoingEncryptedMessage.java @@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.recipients.Recipients; public class OutgoingEncryptedMessage extends OutgoingTextMessage { public OutgoingEncryptedMessage(Recipients recipients, String body) { - super(recipients, body); + super(recipients, body, -1); } private OutgoingEncryptedMessage(OutgoingEncryptedMessage base, String body) { diff --git a/src/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java b/src/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java index a517ebf87e..5a39e155b2 100644 --- a/src/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java +++ b/src/org/thoughtcrime/securesms/sms/OutgoingKeyExchangeMessage.java @@ -5,7 +5,7 @@ import org.thoughtcrime.securesms.recipients.Recipients; public class OutgoingKeyExchangeMessage extends OutgoingTextMessage { public OutgoingKeyExchangeMessage(Recipients recipients, String message) { - super(recipients, message); + super(recipients, message, -1); } private OutgoingKeyExchangeMessage(OutgoingKeyExchangeMessage base, String body) { diff --git a/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java b/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java index c01ad36b0b..cb24306e4e 100644 --- a/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java +++ b/src/org/thoughtcrime/securesms/sms/OutgoingTextMessage.java @@ -7,15 +7,22 @@ public class OutgoingTextMessage { private final Recipients recipients; private final String message; + private final int subscriptionId; - public OutgoingTextMessage(Recipients recipients, String message) { - this.recipients = recipients; - this.message = message; + public OutgoingTextMessage(Recipients recipients, String message, int subscriptionId) { + this.recipients = recipients; + this.message = message; + this.subscriptionId = subscriptionId; } protected OutgoingTextMessage(OutgoingTextMessage base, String body) { - this.recipients = base.getRecipients(); - this.message = body; + this.recipients = base.getRecipients(); + this.subscriptionId = base.getSubscriptionId(); + this.message = body; + } + + public int getSubscriptionId() { + return subscriptionId; } public String getMessageBody() { @@ -48,9 +55,9 @@ public class OutgoingTextMessage { } else if (record.isKeyExchange()) { return new OutgoingKeyExchangeMessage(record.getRecipients(), record.getBody().getBody()); } else if (record.isEndSession()) { - return new OutgoingEndSessionMessage(new OutgoingTextMessage(record.getRecipients(), record.getBody().getBody())); + return new OutgoingEndSessionMessage(new OutgoingTextMessage(record.getRecipients(), record.getBody().getBody(), -1)); } else { - return new OutgoingTextMessage(record.getRecipients(), record.getBody().getBody()); + return new OutgoingTextMessage(record.getRecipients(), record.getBody().getBody(), record.getSubscriptionId()); } } diff --git a/src/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java b/src/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java new file mode 100644 index 0000000000..59ba5e399a --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/dualsim/SubscriptionInfoCompat.java @@ -0,0 +1,23 @@ +package org.thoughtcrime.securesms.util.dualsim; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class SubscriptionInfoCompat { + + private final int subscriptionId; + private final @Nullable CharSequence displayName; + + public SubscriptionInfoCompat(int subscriptionId, @Nullable CharSequence displayName) { + this.subscriptionId = subscriptionId; + this.displayName = displayName; + } + + public @NonNull CharSequence getDisplayName() { + return displayName != null ? displayName : ""; + } + + public int getSubscriptionId() { + return subscriptionId; + } +} diff --git a/src/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java b/src/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java new file mode 100644 index 0000000000..0a29d68d86 --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms.util.dualsim; + +import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; + +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.LinkedList; +import java.util.List; + +public class SubscriptionManagerCompat { + + private final Context context; + + public SubscriptionManagerCompat(Context context) { + this.context = context.getApplicationContext(); + } + + public Optional getActiveSubscriptionInfo(int subscriptionId) { + if (Build.VERSION.SDK_INT < 22) { + return Optional.absent(); + } + + SubscriptionInfo subscriptionInfo = SubscriptionManager.from(context).getActiveSubscriptionInfo(subscriptionId); + + if (subscriptionInfo != null) { + return Optional.of(new SubscriptionInfoCompat(subscriptionId, subscriptionInfo.getDisplayName())); + } else { + return Optional.absent(); + } + } + + public @NonNull List getActiveSubscriptionInfoList() { + if (Build.VERSION.SDK_INT < 22) { + return new LinkedList<>(); + } + + List subscriptionInfos = SubscriptionManager.from(context).getActiveSubscriptionInfoList(); + + if (subscriptionInfos == null || subscriptionInfos.isEmpty()) { + return new LinkedList<>(); + } + + List compatList = new LinkedList<>(); + + for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { + compatList.add(new SubscriptionInfoCompat(subscriptionInfo.getSubscriptionId(), + subscriptionInfo.getDisplayName())); + } + + return compatList; + } + +}