Support for dual-sim SMS/MMS functionality

Allow source selection for sending SMS/MMS, and display the
SIM that received SMS/MMS.

Fixes #555
Closes #5199
// FREEBIE
master
Moxie Marlinspike 2016-02-05 16:10:33 -08:00
parent c1106d98dd
commit 6da86e482d
53 changed files with 727 additions and 281 deletions

View File

@ -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"/>
<TextView android:id="@+id/conversation_item_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingTop="1dip"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?conversation_item_received_text_secondary_color"
android:textSize="@dimen/conversation_item_date_text_size"
android:fontFamily="sans-serif-light"
android:autoLink="none"
android:linksClickable="false"
tools:text="Now"/>
<TextView android:id="@+id/sim_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
@ -137,7 +152,10 @@
android:textSize="@dimen/conversation_item_date_text_size"
android:fontFamily="sans-serif-light"
android:autoLink="none"
android:linksClickable="false" />
android:linksClickable="false"
android:visibility="gone"
tools:visibility="visible"
tools:text="from SIM1"/>
</LinearLayout>
</LinearLayout>

View File

@ -125,6 +125,25 @@
android:paddingBottom="2dp"
tools:text="30 mins" />
<TextView android:id="@+id/sim_info"
android:autoLink="none"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="15sp"
android:linksClickable="false"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_gravity="right"
android:fontFamily="sans-serif-light"
android:textColor="?conversation_item_sent_text_secondary_color"
android:textSize="@dimen/conversation_item_date_text_size"
android:paddingTop="1dip"
android:paddingBottom="2dp"
android:paddingLeft="4dp"
android:paddingStart="4dp"
android:visibility="gone"
tools:visibility="visible"
tools:text="to SIM1"/>
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_width="20dp"

View File

@ -8,6 +8,7 @@
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -16,11 +17,28 @@
android:contentDescription="@string/transport_selection_list_item__transport_icon"
tools:src="@drawable/ic_send_push_white_24dp"
tools:backgroundTint="@color/textsecure_primary" />
<TextView android:id="@+id/text"
android:paddingLeft="10dp"
android:fontFamily="sans-serif-light"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="TextSecure" />
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingLeft="10dp">
<TextView android:id="@+id/text"
android:fontFamily="sans-serif-light"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="TextSecure" />
<TextView android:id="@+id/subtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="12dp"
android:visibility="gone"
tools:text="From Home"
tools:visibility="visible"/>
</LinearLayout>
</LinearLayout>

View File

@ -100,6 +100,8 @@
<string name="ConversationItem_click_to_approve_unencrypted_mms_dialog_title">Fallback to unencrypted MMS?</string>
<string name="ConversationItem_click_to_approve_unencrypted_dialog_message">This message will <b>not</b> be encrypted because the recipient is no longer a Signal user.\n\nSend unsecured message?</string>
<string name="ConversationItem_unable_to_open_media">Can\'t find an app able to open this media.</string>
<string name="ConversationItem_from_s">from %s</string>
<string name="ConversationItem_to_s">to %s</string>
<!-- ConversationActivity -->
<string name="ConversationActivity_reset_secure_session_question">Reset secure session?</string>

View File

@ -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<OutgoingEndSessionMessage, Void, Long>() {
@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<Integer> defaultSubscriptionId) {
Log.w(TAG, "updateDefaultSubscriptionId(" + defaultSubscriptionId.orNull() + ")");
sendButton.setDefaultSubscriptionId(defaultSubscriptionId);
}
private void initializeMmsEnabledCheck() {
new AsyncTask<Void, Void, Boolean>() {
@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<Void> sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck)
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck, final int subscriptionId)
throws InvalidMessageException
{
final SettableFuture<Void> 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<Integer> subscriptionId) {
new AsyncTask<Void, Void, Void>() {
@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<Uri, Long> 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<Void>() {
sendMediaMessage(forceSms, "", slideDeck, subscriptionId).addListener(new AssertedSuccessListener<Void>() {
@Override
public void onSuccess(Void nothing) {
new AsyncTask<Void, Void, Void>() {
@ -1568,30 +1604,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateToggleButtonState();
}
private class ShowInviteReminderTask extends AsyncTask<Recipients, Void, Pair<Recipients,Boolean>> {
private class RecipientPreferencesTask extends AsyncTask<Recipients, Void, Pair<Recipients,RecipientsPreferences>> {
@Override
protected Pair<Recipients, Boolean> doInBackground(Recipients... recipients) {
if (recipients.length != 1 || recipients[0] == null) throw new AssertionError("task needs exactly one Recipients object");
protected Pair<Recipients, RecipientsPreferences> doInBackground(Recipients... recipients) {
if (recipients.length != 1 || recipients[0] == null) {
throw new AssertionError("task needs exactly one Recipients object");
}
Optional<RecipientsPreferences> 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<Recipients, Boolean> 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<Recipients, RecipientsPreferences> result) {
if (result.first == recipients) {
updateInviteReminder(result.second != null && result.second.hasSeenInviteReminder());
updateDefaultSubscriptionId(result.second != null ? result.second.getDefaultSubscriptionId() : Optional.<Integer>absent());
}
}
}

View File

@ -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<SubscriptionInfoCompat> 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();

View File

@ -121,7 +121,7 @@ public class ConversationPopupActivity extends ConversationActivity {
}
@Override
protected void updateInviteReminder() {
protected void updateInviteReminder(boolean seenInvite) {
reminderView.setVisibility(View.GONE);
}
}

View File

@ -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<RecipientsPreferences> 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);
}

View File

@ -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<CharSequence> simName;
private final @NonNull Optional<Integer> 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.<CharSequence>absent(), Optional.<Integer>absent());
}
public TransportOption(@NonNull Type type,
@DrawableRes int drawable,
int backgroundColor,
@NonNull String text,
@NonNull String composeHint,
@NonNull CharacterCalculator characterCalculator,
@NonNull Optional<CharSequence> simName,
@NonNull Optional<Integer> 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<CharSequence> getSimName() {
return simName;
}
@NonNull
public Optional<Integer> getSimSubscriptionId() {
return simSubscriptionId;
}
}

View File

@ -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<TransportOption> enabledTransports;
private Type selectedType;
private boolean manuallySelected;
private Type defaultTransportType = Type.SMS;
private Optional<Integer> defaultSubscriptionId = Optional.absent();
private Optional<TransportOption> 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<TransportOption> 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<Integer> 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<TransportOption> 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<TransportOption> 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<TransportOption> getTransportOptionsForSimCards(@NonNull String text,
@NonNull String composeHint,
@NonNull CharacterCalculator characterCalculator)
{
List<TransportOption> results = new LinkedList<>();
SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(context);
List<SubscriptionInfoCompat> 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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -83,14 +83,18 @@ public class SendButton extends ImageButton
transportOptions.setDefaultTransport(type);
}
public void setDefaultSubscriptionId(Optional<Integer> 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());
}

View File

@ -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();
}

View File

@ -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 {

View File

@ -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<NotificationInd> getNotification(long messageId) {
public Optional<Pair<NotificationInd, Integer>> 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<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(messageId));
MmsAddresses addresses = addr.getAddressesForId(messageId);
List<String> 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<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(messageId));
MmsAddresses addresses = addr.getAddressesForId(messageId);
List<String> 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<Long, Long> insertMessageInbox(@NonNull NotificationInd notification) {
public Pair<Long, Long> 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<IdentityKeyMismatch> 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) {

View File

@ -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;

View File

@ -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);

View File

@ -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<Integer> getDefaultSubscriptionId() {
return defaultSubscriptionId != -1 ? Optional.of(defaultSubscriptionId) : Optional.<Integer>absent();
}
}
}

View File

@ -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<IdentityKeyMismatch> 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) {

View File

@ -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<IdentityKeyMismatch> mismatches,
List<NetworkFailure> failures)
List<NetworkFailure> 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;

View File

@ -50,13 +50,15 @@ public abstract class MessageRecord extends DisplayRecord {
private final long id;
private final List<IdentityKeyMismatch> mismatches;
private final List<NetworkFailure> 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<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures)
List<NetworkFailure> 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;
}
}

View File

@ -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<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>());
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId);
this.contentLocation = contentLocation;
this.messageSize = messageSize;

View File

@ -47,11 +47,12 @@ public class SmsMessageRecord extends MessageRecord {
long dateSent, long dateReceived,
int receiptCount,
long type, long threadId,
int status, List<IdentityKeyMismatch> mismatches)
int status, List<IdentityKeyMismatch> mismatches,
int subscriptionId)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, status, receiptCount, type,
mismatches, new LinkedList<NetworkFailure>());
mismatches, new LinkedList<NetworkFailure>(), subscriptionId);
}
public long getType() {

View File

@ -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);
}

View File

@ -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<NotificationInd> notification = database.getNotification(messageId);
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Optional<Pair<NotificationInd, Integer>> 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<Long, Long> messageAndThreadId = database.insertMessageInbox(new MasterSecretUnion(masterSecret),
message, contentLocation, threadId);

View File

@ -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<Long, Long> messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu);
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu, subscriptionId);
Log.w(TAG, "Inserted received MMS notification...");

View File

@ -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);

View File

@ -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());

View File

@ -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<IncomingTextMessage> message = assembleMessageFragments(pdus);
Optional<IncomingTextMessage> message = assembleMessageFragments(pdus, subscriptionId);
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
MasterSecretUnion masterSecretUnion;
@ -95,11 +99,11 @@ public class SmsReceiveJob extends ContextJob {
return messageAndThreadId;
}
private Optional<IncomingTextMessage> assembleMessageFragments(Object[] pdus) {
private Optional<IncomingTextMessage> assembleMessageFragments(Object[] pdus, int subscriptionId) {
List<IncomingTextMessage> 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()) {

View File

@ -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<messages.size();i++) {
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i),
sentIntents.get(i),
deliveredIntents == null ? null : deliveredIntents.get(i));
getSmsManagerFor(message.getSubscriptionId()).sendTextMessage(recipient, null, messages.get(i),
sentIntents.get(i),
deliveredIntents == null ? null : deliveredIntents.get(i));
}
} catch (NullPointerException npe2) {
Log.w(TAG, npe);
@ -179,6 +180,14 @@ public class SmsSendJob extends SendJob {
return pending;
}
private SmsManager getSmsManagerFor(int subscriptionId) {
if (Build.VERSION.SDK_INT >= 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()

View File

@ -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...");
}
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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<String> to = new LinkedList<>();
private final List<String> cc = new LinkedList<>();
@ -26,13 +27,14 @@ public class IncomingMediaMessage {
public IncomingMediaMessage(String from, List<String> to, List<String> cc,
String body, long sentTimeMillis,
List<Attachment> attachments)
List<Attachment> 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<String> relay,
Optional<String> body,
Optional<TextSecureGroup> 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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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();

View File

@ -14,9 +14,11 @@ public class OutgoingMediaMessage {
protected final List<Attachment> attachments;
private final long sentTimeMillis;
private final int distributionType;
private final int subscriptionId;
public OutgoingMediaMessage(Recipients recipients, String message,
List<Attachment> 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;

View File

@ -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;
}

View File

@ -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) {

View File

@ -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<RecipientsPreferences> 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<Attachment>(), System.currentTimeMillis(), 0);
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), 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);
}

View File

@ -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<String> ids = new LinkedList<>();
@ -81,7 +81,7 @@ public class RecipientFactory {
return getRecipientsForIds(context, ids, asynchronous);
}
public static Recipients getRecipientsFromStrings(@NonNull Context context, @NonNull List<String> numbers, boolean asynchronous) {
public static @NonNull Recipients getRecipientsFromStrings(@NonNull Context context, @NonNull List<String> numbers, boolean asynchronous) {
List<String> 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<String> idStrings, boolean asynchronous) {
private static @NonNull Recipients getRecipientsForIds(Context context, List<String> idStrings, boolean asynchronous) {
long[] ids = new long[idStrings.size()];
int i = 0;

View File

@ -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();
}

View File

@ -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<RecipientsPreferences> 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) {

View File

@ -84,16 +84,6 @@ public class SmsListener extends BroadcastReceiver {
return bodyBuilder.toString();
}
// private ArrayList<IncomingTextMessage> getAsTextMessages(Intent intent) {
// Object[] pdus = (Object[])intent.getExtras().get("pdus");
// ArrayList<IncomingTextMessage> messages = new ArrayList<IncomingTextMessage>(pdus.length);
//
// for (int i=0;i<pdus.length;i++)
// messages.add(new IncomingTextMessage(SmsMessage.createFromPdu((byte[])pdus[i])));
//
// return messages;
// }
private boolean isRelevant(Context context, Intent intent) {
SmsMessage message = getSmsMessageFromIntent(intent);
String messageBody = getSmsMessageBodyFromIntent(intent);
@ -164,14 +154,10 @@ public class SmsListener extends BroadcastReceiver {
} else if ((intent.getAction().equals(SMS_DELIVERED_ACTION)) ||
(intent.getAction().equals(SMS_RECEIVED_ACTION)) && isRelevant(context, intent))
{
Object[] pdus = (Object[])intent.getExtras().get("pdus");
ApplicationContext.getInstance(context).getJobManager().add(new SmsReceiveJob(context, pdus));
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
int subscriptionId = intent.getExtras().getInt("subscription", -1);
// Intent receivedIntent = new Intent(context, SendReceiveService.class);
// receivedIntent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
// receivedIntent.putExtra("ResultCode", this.getResultCode());
// receivedIntent.putParcelableArrayListExtra("text_messages",getAsTextMessages(intent));
// context.startService(receivedIntent);
ApplicationContext.getInstance(context).getJobManager().add(new SmsReceiveJob(context, pdus, subscriptionId));
abortBroadcast();
}

View File

@ -35,8 +35,9 @@ public class IncomingTextMessage implements Parcelable {
private final long sentTimestampMillis;
private final String groupId;
private final boolean push;
private final int subscriptionId;
public IncomingTextMessage(SmsMessage message) {
public IncomingTextMessage(SmsMessage message, int subscriptionId) {
this.message = message.getDisplayMessageBody();
this.sender = message.getDisplayOriginatingAddress();
this.senderDeviceId = TextSecureAddress.DEFAULT_DEVICE_ID;
@ -45,6 +46,7 @@ public class IncomingTextMessage implements Parcelable {
this.replyPathPresent = message.isReplyPathPresent();
this.pseudoSubject = message.getPseudoSubject();
this.sentTimestampMillis = message.getTimestampMillis();
this.subscriptionId = subscriptionId;
this.groupId = null;
this.push = false;
}
@ -61,6 +63,7 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = "";
this.sentTimestampMillis = sentTimestampMillis;
this.push = true;
this.subscriptionId = -1;
if (group.isPresent()) {
this.groupId = GroupUtil.getEncodedId(group.get().getGroupId());
@ -80,6 +83,7 @@ public class IncomingTextMessage implements Parcelable {
this.sentTimestampMillis = in.readLong();
this.groupId = in.readString();
this.push = (in.readInt() == 1);
this.subscriptionId = in.readInt();
}
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
@ -93,6 +97,7 @@ public class IncomingTextMessage implements Parcelable {
this.sentTimestampMillis = base.getSentTimestampMillis();
this.groupId = base.getGroupId();
this.push = base.isPush();
this.subscriptionId = base.getSubscriptionId();
}
public IncomingTextMessage(List<IncomingTextMessage> 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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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<SubscriptionInfoCompat> 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<SubscriptionInfoCompat> getActiveSubscriptionInfoList() {
if (Build.VERSION.SDK_INT < 22) {
return new LinkedList<>();
}
List<SubscriptionInfo> subscriptionInfos = SubscriptionManager.from(context).getActiveSubscriptionInfoList();
if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
return new LinkedList<>();
}
List<SubscriptionInfoCompat> compatList = new LinkedList<>();
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
compatList.add(new SubscriptionInfoCompat(subscriptionInfo.getSubscriptionId(),
subscriptionInfo.getDisplayName()));
}
return compatList;
}
}