Refactor how message send types are selected.
This commit is contained in:
parent
bf90909496
commit
4da422fd3c
29 changed files with 591 additions and 808 deletions
|
@ -1,149 +0,0 @@
|
|||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.util.CharacterCalculator;
|
||||
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
public class TransportOption implements Parcelable {
|
||||
|
||||
public enum Type {
|
||||
SMS,
|
||||
TEXTSECURE
|
||||
}
|
||||
|
||||
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(@NonNull Type type,
|
||||
@DrawableRes int drawable,
|
||||
int backgroundColor,
|
||||
@NonNull String text,
|
||||
@NonNull String composeHint,
|
||||
@NonNull CharacterCalculator characterCalculator)
|
||||
{
|
||||
this(type, drawable, backgroundColor, text, composeHint, characterCalculator,
|
||||
Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
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;
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.text = text;
|
||||
this.composeHint = composeHint;
|
||||
this.characterCalculator = characterCalculator;
|
||||
this.simName = simName;
|
||||
this.simSubscriptionId = simSubscriptionId;
|
||||
}
|
||||
|
||||
TransportOption(Parcel in) {
|
||||
this(Type.valueOf(in.readString()),
|
||||
in.readInt(),
|
||||
in.readInt(),
|
||||
in.readString(),
|
||||
in.readString(),
|
||||
CharacterCalculator.readFromParcel(in),
|
||||
Optional.ofNullable(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)),
|
||||
in.readInt() == 1 ? Optional.of(in.readInt()) : Optional.empty());
|
||||
}
|
||||
|
||||
public @NonNull Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isType(Type type) {
|
||||
return this.type == type;
|
||||
}
|
||||
|
||||
public boolean isSms() {
|
||||
return type == Type.SMS;
|
||||
}
|
||||
|
||||
public CharacterState calculateCharacters(String messageBody) {
|
||||
return characterCalculator.calculateCharacters(messageBody);
|
||||
}
|
||||
|
||||
public @DrawableRes int getDrawable() {
|
||||
return drawable;
|
||||
}
|
||||
|
||||
public int getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
public @NonNull String getComposeHint() {
|
||||
return composeHint;
|
||||
}
|
||||
|
||||
public @NonNull String getDescription() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Optional<CharSequence> getSimName() {
|
||||
return simName;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Optional<Integer> getSimSubscriptionId() {
|
||||
return simSubscriptionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(type.name());
|
||||
dest.writeInt(drawable);
|
||||
dest.writeInt(backgroundColor);
|
||||
dest.writeString(text);
|
||||
dest.writeString(composeHint);
|
||||
CharacterCalculator.writeToParcel(dest, characterCalculator);
|
||||
TextUtils.writeToParcel(simName.orElse(null), dest, flags);
|
||||
|
||||
if (simSubscriptionId.isPresent()) {
|
||||
dest.writeInt(1);
|
||||
dest.writeInt(simSubscriptionId.get());
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<TransportOption> CREATOR = new Creator<TransportOption>() {
|
||||
@Override
|
||||
public TransportOption createFromParcel(Parcel in) {
|
||||
return new TransportOption(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportOption[] newArray(int size) {
|
||||
return new TransportOption[size];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
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.signalservice.api.util.OptionalUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.thoughtcrime.securesms.TransportOption.Type;
|
||||
|
||||
public class TransportOptions {
|
||||
|
||||
private static final String TAG = Log.tag(TransportOptions.class);
|
||||
|
||||
private final List<OnTransportChangedListener> listeners = new LinkedList<>();
|
||||
private final Context context;
|
||||
private final List<TransportOption> enabledTransports;
|
||||
|
||||
private Type defaultTransportType = Type.SMS;
|
||||
private Optional<Integer> defaultSubscriptionId = Optional.empty();
|
||||
private Optional<TransportOption> selectedOption = Optional.empty();
|
||||
|
||||
private final Optional<Integer> systemSubscriptionId;
|
||||
|
||||
public TransportOptions(Context context, boolean media) {
|
||||
this.context = context;
|
||||
this.enabledTransports = initializeAvailableTransports(media);
|
||||
this.systemSubscriptionId = new SubscriptionManagerCompat(context).getPreferredSubscriptionId();
|
||||
}
|
||||
|
||||
public void reset(boolean media) {
|
||||
List<TransportOption> transportOptions = initializeAvailableTransports(media);
|
||||
|
||||
this.enabledTransports.clear();
|
||||
this.enabledTransports.addAll(transportOptions);
|
||||
|
||||
if (selectedOption.isPresent() && !isEnabled(selectedOption.get())) {
|
||||
setSelectedTransport(null);
|
||||
} else {
|
||||
this.defaultTransportType = Type.SMS;
|
||||
this.defaultSubscriptionId = Optional.empty();
|
||||
|
||||
notifyTransportChangeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultTransport(Type type) {
|
||||
this.defaultTransportType = type;
|
||||
|
||||
if (!selectedOption.isPresent()) {
|
||||
notifyTransportChangeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultSubscriptionId(Optional<Integer> subscriptionId) {
|
||||
if (defaultSubscriptionId.equals(subscriptionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.defaultSubscriptionId = subscriptionId;
|
||||
|
||||
if (!selectedOption.isPresent()) {
|
||||
notifyTransportChangeListeners();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedTransport(@Nullable TransportOption transportOption) {
|
||||
this.selectedOption = Optional.ofNullable(transportOption);
|
||||
notifyTransportChangeListeners();
|
||||
}
|
||||
|
||||
public boolean isManualSelection() {
|
||||
return this.selectedOption.isPresent();
|
||||
}
|
||||
|
||||
public @NonNull TransportOption getSelectedTransport() {
|
||||
if (selectedOption.isPresent()) return selectedOption.get();
|
||||
|
||||
if (defaultTransportType == Type.SMS) {
|
||||
TransportOption transportOption = findEnabledSmsTransportOption(OptionalUtil.or(defaultSubscriptionId, systemSubscriptionId));
|
||||
if (transportOption != null) {
|
||||
return transportOption;
|
||||
}
|
||||
}
|
||||
|
||||
for (TransportOption transportOption : enabledTransports) {
|
||||
if (transportOption.getType() == defaultTransportType) {
|
||||
return transportOption;
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError("No options of default type!");
|
||||
}
|
||||
|
||||
public static @NonNull TransportOption getPushTransportOption(@NonNull Context context) {
|
||||
return new TransportOption(Type.TEXTSECURE,
|
||||
R.drawable.ic_send_lock_24,
|
||||
context.getResources().getColor(R.color.core_ultramarine),
|
||||
context.getString(R.string.ConversationActivity_transport_signal),
|
||||
context.getString(R.string.conversation_activity__type_message_push),
|
||||
new PushCharacterCalculator());
|
||||
|
||||
}
|
||||
|
||||
private @Nullable TransportOption findEnabledSmsTransportOption(Optional<Integer> subscriptionId) {
|
||||
if (subscriptionId.isPresent()) {
|
||||
final int subId = subscriptionId.get();
|
||||
|
||||
for (TransportOption transportOption : enabledTransports) {
|
||||
if (transportOption.getType() == Type.SMS &&
|
||||
subId == transportOption.getSimSubscriptionId().orElse(-1)) {
|
||||
return transportOption;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void disableTransport(Type type) {
|
||||
TransportOption selected = selectedOption.orElse(null);
|
||||
|
||||
Iterator<TransportOption> iterator = enabledTransports.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
TransportOption option = iterator.next();
|
||||
|
||||
if (option.isType(type)) {
|
||||
if (selected == option) {
|
||||
setSelectedTransport(null);
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<TransportOption> getEnabledTransports() {
|
||||
return enabledTransports;
|
||||
}
|
||||
|
||||
public void addOnTransportChangedListener(OnTransportChangedListener listener) {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
private List<TransportOption> initializeAvailableTransports(boolean isMediaMessage) {
|
||||
List<TransportOption> results = new LinkedList<>();
|
||||
|
||||
if (isMediaMessage) {
|
||||
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.addAll(getTransportOptionsForSimCards(context.getString(R.string.ConversationActivity_transport_insecure_sms),
|
||||
context.getString(R.string.conversation_activity__type_message_sms_insecure),
|
||||
new SmsCharacterCalculator()));
|
||||
}
|
||||
|
||||
results.add(getPushTransportOption(context));
|
||||
|
||||
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);
|
||||
Collection<SubscriptionInfoCompat> subscriptions;
|
||||
|
||||
if (Permissions.hasAll(context, Manifest.permission.READ_PHONE_STATE)) {
|
||||
subscriptions = subscriptionManager.getActiveAndReadySubscriptionInfos();
|
||||
} else {
|
||||
subscriptions = Collections.emptyList();
|
||||
}
|
||||
|
||||
if (subscriptions.size() < 2) {
|
||||
results.add(new TransportOption(Type.SMS, R.drawable.ic_send_unlock_24,
|
||||
context.getResources().getColor(R.color.core_grey_50),
|
||||
text, composeHint, characterCalculator));
|
||||
} else {
|
||||
for (SubscriptionInfoCompat subscriptionInfo : subscriptions) {
|
||||
results.add(new TransportOption(Type.SMS, R.drawable.ic_send_unlock_24,
|
||||
context.getResources().getColor(R.color.core_grey_50),
|
||||
text, composeHint, characterCalculator,
|
||||
Optional.of(subscriptionInfo.getDisplayName()),
|
||||
Optional.of(subscriptionInfo.getSubscriptionId())));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private void notifyTransportChangeListeners() {
|
||||
for (OnTransportChangedListener listener : listeners) {
|
||||
listener.onChange(getSelectedTransport(), selectedOption.isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
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, boolean manuallySelected);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TransportOptionsAdapter extends BaseAdapter {
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
|
||||
private List<TransportOption> enabledTransports;
|
||||
|
||||
public TransportOptionsAdapter(@NonNull Context context,
|
||||
@NonNull List<TransportOption> enabledTransports)
|
||||
{
|
||||
super();
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.enabledTransports = enabledTransports;
|
||||
}
|
||||
|
||||
public void setEnabledTransports(List<TransportOption> enabledTransports) {
|
||||
this.enabledTransports = enabledTransports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return enabledTransports.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return enabledTransports.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = inflater.inflate(R.layout.transport_selection_list_item, parent, false);
|
||||
}
|
||||
|
||||
TransportOption transport = (TransportOption) getItem(position);
|
||||
ImageView imageView = convertView.findViewById(R.id.icon);
|
||||
TextView textView = convertView.findViewById(R.id.text);
|
||||
TextView subtextView = convertView.findViewById(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;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.ListPopupWindow;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class TransportOptionsPopup extends ListPopupWindow implements ListView.OnItemClickListener {
|
||||
|
||||
private final TransportOptionsAdapter adapter;
|
||||
private final SelectedListener listener;
|
||||
|
||||
public TransportOptionsPopup(@NonNull Context context, @NonNull View anchor, @NonNull SelectedListener listener) {
|
||||
super(context);
|
||||
this.listener = listener;
|
||||
this.adapter = new TransportOptionsAdapter(context, new LinkedList<TransportOption>());
|
||||
|
||||
setVerticalOffset(context.getResources().getDimensionPixelOffset(R.dimen.transport_selection_popup_yoff));
|
||||
setHorizontalOffset(context.getResources().getDimensionPixelOffset(R.dimen.transport_selection_popup_xoff));
|
||||
setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
|
||||
setModal(true);
|
||||
setAnchorView(anchor);
|
||||
setAdapter(adapter);
|
||||
setContentWidth(context.getResources().getDimensionPixelSize(R.dimen.transport_selection_popup_width));
|
||||
|
||||
setOnItemClickListener(this);
|
||||
}
|
||||
|
||||
public void display(List<TransportOption> enabledTransports) {
|
||||
adapter.setEnabledTransports(enabledTransports);
|
||||
adapter.notifyDataSetChanged();
|
||||
show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
listener.onSelected((TransportOption)adapter.getItem(position));
|
||||
}
|
||||
|
||||
public interface SelectedListener {
|
||||
void onSelected(TransportOption option);
|
||||
}
|
||||
|
||||
}
|
|
@ -28,12 +28,12 @@ import androidx.core.view.inputmethod.InputContentInfoCompat;
|
|||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiEditText;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionDeleter;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionRendererDelegate;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionValidatorWatcher;
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
@ -201,13 +201,13 @@ public class ComposeText extends EmojiEditText {
|
|||
return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
|
||||
public void setTransport(TransportOption transport) {
|
||||
public void setMessageSendType(MessageSendType messageSendType) {
|
||||
final boolean useSystemEmoji = SignalStore.settings().isPreferSystemEmoji();
|
||||
|
||||
int imeOptions = (getImeOptions() & ~EditorInfo.IME_MASK_ACTION) | EditorInfo.IME_ACTION_SEND;
|
||||
int inputType = getInputType();
|
||||
|
||||
if (isLandscape()) setImeActionLabel(transport.getComposeHint(), EditorInfo.IME_ACTION_SEND);
|
||||
if (isLandscape()) setImeActionLabel(getContext().getString(messageSendType.getComposeHintRes()), EditorInfo.IME_ACTION_SEND);
|
||||
else setImeActionLabel(null, 0);
|
||||
|
||||
if (useSystemEmoji) {
|
||||
|
@ -215,9 +215,9 @@ public class ComposeText extends EmojiEditText {
|
|||
}
|
||||
|
||||
setImeOptions(imeOptions);
|
||||
setHint(transport.getComposeHint(),
|
||||
transport.getSimName().isPresent()
|
||||
? getContext().getString(R.string.conversation_activity__from_sim_name, transport.getSimName().get())
|
||||
setHint(getContext().getString(messageSendType.getComposeHintRes()),
|
||||
messageSendType.getSimName() != null
|
||||
? getContext().getString(R.string.conversation_activity__from_sim_name, messageSendType.getSimName())
|
||||
: null);
|
||||
setInputType(inputType);
|
||||
}
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.TransportOptions;
|
||||
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
|
||||
import org.thoughtcrime.securesms.TransportOptionsPopup;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
public class SendButton extends AppCompatImageButton
|
||||
implements TransportOptions.OnTransportChangedListener,
|
||||
TransportOptionsPopup.SelectedListener,
|
||||
View.OnLongClickListener
|
||||
{
|
||||
|
||||
private final TransportOptions transportOptions;
|
||||
|
||||
private Optional<TransportOptionsPopup> transportOptionsPopup = Optional.empty();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public SendButton(Context context) {
|
||||
super(context);
|
||||
this.transportOptions = initializeTransportOptions(false);
|
||||
ViewUtil.mirrorIfRtl(this, getContext());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public SendButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.transportOptions = initializeTransportOptions(false);
|
||||
ViewUtil.mirrorIfRtl(this, getContext());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public SendButton(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
this.transportOptions = initializeTransportOptions(false);
|
||||
ViewUtil.mirrorIfRtl(this, getContext());
|
||||
}
|
||||
|
||||
private TransportOptions initializeTransportOptions(boolean media) {
|
||||
if (isInEditMode()) return null;
|
||||
|
||||
TransportOptions transportOptions = new TransportOptions(getContext(), media);
|
||||
transportOptions.addOnTransportChangedListener(this);
|
||||
|
||||
setOnLongClickListener(this);
|
||||
|
||||
return transportOptions;
|
||||
}
|
||||
|
||||
private TransportOptionsPopup getTransportOptionsPopup() {
|
||||
if (!transportOptionsPopup.isPresent()) {
|
||||
transportOptionsPopup = Optional.of(new TransportOptionsPopup(getContext(), this, this));
|
||||
}
|
||||
return transportOptionsPopup.get();
|
||||
}
|
||||
|
||||
public boolean isManualSelection() {
|
||||
return transportOptions.isManualSelection();
|
||||
}
|
||||
|
||||
public void addOnTransportChangedListener(OnTransportChangedListener listener) {
|
||||
transportOptions.addOnTransportChangedListener(listener);
|
||||
}
|
||||
|
||||
public TransportOption getSelectedTransport() {
|
||||
return transportOptions.getSelectedTransport();
|
||||
}
|
||||
|
||||
public void resetAvailableTransports(boolean isMediaMessage) {
|
||||
transportOptions.reset(isMediaMessage);
|
||||
}
|
||||
|
||||
public void disableTransport(TransportOption.Type type) {
|
||||
transportOptions.disableTransport(type);
|
||||
}
|
||||
|
||||
public void setDefaultTransport(TransportOption.Type type) {
|
||||
transportOptions.setDefaultTransport(type);
|
||||
}
|
||||
|
||||
public void setTransport(@NonNull TransportOption option) {
|
||||
transportOptions.setSelectedTransport(option);
|
||||
}
|
||||
|
||||
public void setDefaultSubscriptionId(Optional<Integer> subscriptionId) {
|
||||
transportOptions.setDefaultSubscriptionId(subscriptionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelected(TransportOption option) {
|
||||
transportOptions.setSelectedTransport(option);
|
||||
getTransportOptionsPopup().dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(TransportOption newTransport, boolean isManualSelection) {
|
||||
setImageResource(newTransport.getDrawable());
|
||||
setContentDescription(newTransport.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (isEnabled() && transportOptions.getEnabledTransports().size() > 1) {
|
||||
getTransportOptionsPopup().display(transportOptions.getEnabledTransports());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package org.thoughtcrime.securesms.components
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.View.OnLongClickListener
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.components.menu.ActionItem
|
||||
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import java.lang.AssertionError
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
/**
|
||||
* The send button you see in a conversation.
|
||||
* Also encapsulates the long-press menu that allows users to switch [MessageSendType]s.
|
||||
*/
|
||||
class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImageButton(context, attributeSet), OnLongClickListener {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(SendButton::class.java)
|
||||
}
|
||||
|
||||
private val listeners: MutableList<SendTypeChangedListener> = CopyOnWriteArrayList()
|
||||
|
||||
private var availableSendTypes: List<MessageSendType> = MessageSendType.getAllAvailable(context, false)
|
||||
private var activeMessageSendType: MessageSendType? = null
|
||||
private var defaultTransportType: MessageSendType.TransportType = MessageSendType.TransportType.SMS
|
||||
private var defaultSubscriptionId: Int? = null
|
||||
private var popupContainer: ViewGroup? = null
|
||||
|
||||
init {
|
||||
setOnLongClickListener(this)
|
||||
ViewUtil.mirrorIfRtl(this, getContext())
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the [selectedSendType] was chosen manually by the user, otherwise false.
|
||||
*/
|
||||
val isManualSelection: Boolean
|
||||
get() = activeMessageSendType != null
|
||||
|
||||
/**
|
||||
* The actively-selected send type.
|
||||
*/
|
||||
val selectedSendType: MessageSendType
|
||||
get() {
|
||||
activeMessageSendType?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
if (defaultTransportType === MessageSendType.TransportType.SMS) {
|
||||
for (type in availableSendTypes) {
|
||||
if (type.usesSmsTransport && (defaultSubscriptionId == null || type.simSubscriptionId == defaultSubscriptionId)) {
|
||||
return type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (type in availableSendTypes) {
|
||||
if (type.transportType === defaultTransportType) {
|
||||
return type
|
||||
}
|
||||
}
|
||||
|
||||
throw AssertionError("No options of default type!")
|
||||
}
|
||||
|
||||
fun addOnSelectionChangedListener(listener: SendTypeChangedListener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
fun triggerSelectedChangedEvent() {
|
||||
onSelectionChanged(newType = selectedSendType, isManualSelection = false)
|
||||
}
|
||||
|
||||
fun resetAvailableTransports(isMediaMessage: Boolean) {
|
||||
availableSendTypes = MessageSendType.getAllAvailable(context, isMediaMessage)
|
||||
|
||||
if (!availableSendTypes.contains(activeMessageSendType)) {
|
||||
Log.w(TAG, "[resetAvailableTransports] The active send type is no longer available. Unsetting.")
|
||||
setSendType(null)
|
||||
} else {
|
||||
defaultTransportType = MessageSendType.TransportType.SMS
|
||||
defaultSubscriptionId = null
|
||||
onSelectionChanged(newType = selectedSendType, isManualSelection = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun disableTransportType(type: MessageSendType.TransportType) {
|
||||
availableSendTypes = availableSendTypes.filterNot { it.transportType == type }
|
||||
}
|
||||
|
||||
fun setDefaultTransport(type: MessageSendType.TransportType) {
|
||||
if (defaultTransportType == type) {
|
||||
return
|
||||
}
|
||||
defaultTransportType = type
|
||||
onSelectionChanged(newType = selectedSendType, isManualSelection = false)
|
||||
}
|
||||
|
||||
fun setSendType(sendType: MessageSendType?) {
|
||||
if (activeMessageSendType == sendType) {
|
||||
return
|
||||
}
|
||||
activeMessageSendType = sendType
|
||||
onSelectionChanged(newType = selectedSendType, isManualSelection = true)
|
||||
}
|
||||
|
||||
fun setDefaultSubscriptionId(subscriptionId: Int?) {
|
||||
if (defaultSubscriptionId == subscriptionId) {
|
||||
return
|
||||
}
|
||||
defaultSubscriptionId = subscriptionId
|
||||
onSelectionChanged(newType = selectedSendType, isManualSelection = false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called with a view that is acceptable for determining the bounds of the popup selector.
|
||||
*/
|
||||
fun setPopupContainer(container: ViewGroup) {
|
||||
popupContainer = container
|
||||
}
|
||||
|
||||
private fun onSelectionChanged(newType: MessageSendType, isManualSelection: Boolean) {
|
||||
setImageResource(newType.buttonDrawableRes)
|
||||
contentDescription = context.getString(newType.titleRes)
|
||||
|
||||
for (listener in listeners) {
|
||||
listener.onSendTypeChanged(newType, isManualSelection)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
if (!isEnabled || availableSendTypes.size == 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
val currentlySelected: MessageSendType = selectedSendType
|
||||
|
||||
val items = availableSendTypes
|
||||
.filterNot { it == currentlySelected }
|
||||
.map { option ->
|
||||
ActionItem(
|
||||
iconRes = option.menuDrawableRes,
|
||||
title = option.getTitle(context),
|
||||
action = { setSendType(option) }
|
||||
)
|
||||
}
|
||||
|
||||
SignalContextMenu.Builder((parent as View), popupContainer!!)
|
||||
.preferredVerticalPosition(SignalContextMenu.VerticalPosition.ABOVE)
|
||||
.offsetY(ViewUtil.dpToPx(8))
|
||||
.show(items)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
interface SendTypeChangedListener {
|
||||
fun onSendTypeChanged(newType: MessageSendType, manuallySelected: Boolean)
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ class SignalContextMenu private constructor(
|
|||
val baseOffsetX: Int = 0,
|
||||
val baseOffsetY: Int = 0,
|
||||
val horizontalPosition: HorizontalPosition = HorizontalPosition.START,
|
||||
val verticalPosition: VerticalPosition = VerticalPosition.BELOW,
|
||||
val onDismiss: Runnable? = null
|
||||
) : PopupWindow(
|
||||
LayoutInflater.from(anchor.context).inflate(R.layout.signal_context_menu, null),
|
||||
|
@ -41,6 +42,7 @@ class SignalContextMenu private constructor(
|
|||
|
||||
init {
|
||||
setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.signal_context_menu_background))
|
||||
inputMethodMode = INPUT_METHOD_NOT_NEEDED
|
||||
|
||||
isFocusable = true
|
||||
|
||||
|
@ -80,7 +82,10 @@ class SignalContextMenu private constructor(
|
|||
|
||||
val offsetY: Int
|
||||
|
||||
if (menuBottomBound < screenBottomBound) {
|
||||
if (verticalPosition == VerticalPosition.ABOVE && menuTopBound > screenTopBound) {
|
||||
offsetY = -(anchorRect.height() + contentView.measuredHeight + baseOffsetY)
|
||||
contextMenuList.setItems(items.reversed())
|
||||
} else if (menuBottomBound < screenBottomBound) {
|
||||
offsetY = baseOffsetY
|
||||
} else if (menuTopBound > screenTopBound) {
|
||||
offsetY = -(anchorRect.height() + contentView.measuredHeight + baseOffsetY)
|
||||
|
@ -115,6 +120,10 @@ class SignalContextMenu private constructor(
|
|||
START, END
|
||||
}
|
||||
|
||||
enum class VerticalPosition {
|
||||
ABOVE, BELOW
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anchor The view to put the pop-up on
|
||||
* @param container A parent of [anchor] that represents the acceptable boundaries of the popup
|
||||
|
@ -128,6 +137,7 @@ class SignalContextMenu private constructor(
|
|||
var offsetX = 0
|
||||
var offsetY = 0
|
||||
var horizontalPosition = HorizontalPosition.START
|
||||
var verticalPosition = VerticalPosition.BELOW
|
||||
|
||||
fun onDismiss(onDismiss: Runnable): Builder {
|
||||
this.onDismiss = onDismiss
|
||||
|
@ -149,6 +159,11 @@ class SignalContextMenu private constructor(
|
|||
return this
|
||||
}
|
||||
|
||||
fun preferredVerticalPosition(verticalPosition: VerticalPosition): Builder {
|
||||
this.verticalPosition = verticalPosition
|
||||
return this
|
||||
}
|
||||
|
||||
fun show(items: List<ActionItem>): SignalContextMenu {
|
||||
return SignalContextMenu(
|
||||
anchor = anchor,
|
||||
|
@ -157,6 +172,7 @@ class SignalContextMenu private constructor(
|
|||
baseOffsetX = offsetX,
|
||||
baseOffsetY = offsetY,
|
||||
horizontalPosition = horizontalPosition,
|
||||
verticalPosition = verticalPosition,
|
||||
onDismiss = onDismiss
|
||||
).show()
|
||||
}
|
||||
|
|
|
@ -115,7 +115,6 @@ import org.thoughtcrime.securesms.MuteDialog;
|
|||
import org.thoughtcrime.securesms.PromptMmsActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.ShortcutLauncherActivity;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
|
||||
import org.thoughtcrime.securesms.audio.AudioRecorder;
|
||||
|
@ -328,7 +327,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
|
||||
import kotlin.Unit;
|
||||
|
||||
import static org.thoughtcrime.securesms.TransportOption.Type;
|
||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
|
||||
/**
|
||||
|
@ -590,6 +588,8 @@ public class ConversationParentFragment extends Fragment
|
|||
if (isSearchRequested && savedInstanceState == null) {
|
||||
onCreateOptionsMenu(toolbar.getMenu(), requireActivity().getMenuInflater());
|
||||
}
|
||||
|
||||
sendButton.post(() -> sendButton.triggerSelectedChangedEvent());
|
||||
}
|
||||
|
||||
// TODO [alex] LargeScreenSupport -- This needs to be fed a stream of intents
|
||||
|
@ -672,7 +672,7 @@ public class ConversationParentFragment extends Fragment
|
|||
EventBus.getDefault().register(this);
|
||||
initializeMmsEnabledCheck();
|
||||
initializeIdentityRecords();
|
||||
composeText.setTransport(sendButton.getSelectedTransport());
|
||||
composeText.setMessageSendType(sendButton.getSelectedSendType());
|
||||
|
||||
Recipient recipientSnapshot = recipient.get();
|
||||
|
||||
|
@ -731,7 +731,7 @@ public class ConversationParentFragment extends Fragment
|
|||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")");
|
||||
super.onConfigurationChanged(newConfig);
|
||||
composeText.setTransport(sendButton.getSelectedTransport());
|
||||
composeText.setMessageSendType(sendButton.getSelectedSendType());
|
||||
|
||||
if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) {
|
||||
container.hideAttachedInput(true);
|
||||
|
@ -823,7 +823,7 @@ public class ConversationParentFragment extends Fragment
|
|||
return;
|
||||
}
|
||||
|
||||
sendButton.setTransport(result.getTransport());
|
||||
sendButton.setSendType(result.getMessageSendType());
|
||||
|
||||
if (result.isPushPreUpload()) {
|
||||
sendMediaMessage(result);
|
||||
|
@ -831,7 +831,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds());
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1);
|
||||
int subscriptionId = sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1);
|
||||
boolean initiating = threadId == -1;
|
||||
QuoteModel quote = result.isViewOnce() ? null : inputPanel.getQuote().orElse(null);
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
|
@ -852,7 +852,7 @@ public class ConversationParentFragment extends Fragment
|
|||
final Context context = requireContext().getApplicationContext();
|
||||
|
||||
sendMediaMessage(result.getRecipientId(),
|
||||
result.getTransport().isSms(),
|
||||
result.getMessageSendType().usesSmsTransport(),
|
||||
result.getBody(),
|
||||
slideDeck,
|
||||
quote,
|
||||
|
@ -1231,7 +1231,7 @@ public class ConversationParentFragment extends Fragment
|
|||
@Override
|
||||
public void onAttachmentMediaClicked(@NonNull Media media) {
|
||||
linkPreviewViewModel.onUserCancel();
|
||||
startActivityForResult(MediaSelectionActivity.editor(requireActivity(), sendButton.getSelectedTransport(), Collections.singletonList(media), recipient.getId(), composeText.getTextTrimmed()), MEDIA_SENDER);
|
||||
startActivityForResult(MediaSelectionActivity.editor(requireActivity(), sendButton.getSelectedSendType(), Collections.singletonList(media), recipient.getId(), composeText.getTextTrimmed()), MEDIA_SENDER);
|
||||
container.hideCurrentInput(composeText);
|
||||
}
|
||||
|
||||
|
@ -1239,7 +1239,7 @@ public class ConversationParentFragment extends Fragment
|
|||
public void onAttachmentSelectorClicked(@NonNull AttachmentKeyboardButton button) {
|
||||
switch (button) {
|
||||
case GALLERY:
|
||||
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport(), inputPanel.getQuote().isPresent());
|
||||
AttachmentManager.selectGallery(this, MEDIA_SENDER, recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedSendType(), inputPanel.getQuote().isPresent());
|
||||
break;
|
||||
case FILE:
|
||||
AttachmentManager.selectDocument(this, PICK_DOCUMENT);
|
||||
|
@ -1629,21 +1629,21 @@ public class ConversationParentFragment extends Fragment
|
|||
boolean smsEnabled = true;
|
||||
|
||||
if (recipient.get().isPushGroup() || (!recipient.get().isMmsGroup() && !recipient.get().hasSmsAddress())) {
|
||||
sendButton.disableTransport(Type.SMS);
|
||||
sendButton.disableTransportType(MessageSendType.TransportType.SMS);
|
||||
smsEnabled = false;
|
||||
}
|
||||
|
||||
if (!isSecureText && !isPushGroupConversation() && !recipient.get().isServiceIdOnly() && !recipient.get().isReleaseNotes() && smsEnabled) {
|
||||
sendButton.disableTransport(Type.TEXTSECURE);
|
||||
sendButton.disableTransportType(MessageSendType.TransportType.SIGNAL);
|
||||
}
|
||||
|
||||
if (!recipient.get().isPushGroup() && recipient.get().isForceSmsSelection() && smsEnabled) {
|
||||
sendButton.setDefaultTransport(Type.SMS);
|
||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
||||
} else {
|
||||
if (isSecureText || isPushGroupConversation() || recipient.get().isServiceIdOnly() || recipient.get().isReleaseNotes() || !smsEnabled) {
|
||||
sendButton.setDefaultTransport(Type.TEXTSECURE);
|
||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SIGNAL);
|
||||
} else {
|
||||
sendButton.setDefaultTransport(Type.SMS);
|
||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1680,7 +1680,7 @@ public class ConversationParentFragment extends Fragment
|
|||
|
||||
if (!Util.isEmpty(mediaList)) {
|
||||
Log.d(TAG, "Handling shared Media.");
|
||||
Intent sendIntent = MediaSelectionActivity.editor(requireContext(), sendButton.getSelectedTransport(), mediaList, recipient.getId(), draftText);
|
||||
Intent sendIntent = MediaSelectionActivity.editor(requireContext(), sendButton.getSelectedSendType(), mediaList, recipient.getId(), draftText);
|
||||
startActivityForResult(sendIntent, MEDIA_SENDER);
|
||||
return new SettableFuture<>(false);
|
||||
}
|
||||
|
@ -2029,7 +2029,7 @@ public class ConversationParentFragment extends Fragment
|
|||
|
||||
private void updateDefaultSubscriptionId(Optional<Integer> defaultSubscriptionId) {
|
||||
Log.i(TAG, "updateDefaultSubscriptionId(" + defaultSubscriptionId.orElse(null) + ")");
|
||||
sendButton.setDefaultSubscriptionId(defaultSubscriptionId);
|
||||
sendButton.setDefaultSubscriptionId(defaultSubscriptionId.orElse(null));
|
||||
}
|
||||
|
||||
private void initializeMmsEnabledCheck() {
|
||||
|
@ -2150,6 +2150,8 @@ public class ConversationParentFragment extends Fragment
|
|||
releaseChannelUnmute = ViewUtil.findStubById(view, R.id.conversation_release_notes_unmute_stub);
|
||||
joinGroupCallButton = view.findViewById(R.id.conversation_group_call_join);
|
||||
|
||||
sendButton.setPopupContainer((ViewGroup) view);
|
||||
|
||||
container.setIsBubble(isInBubble());
|
||||
container.addOnKeyboardShownListener(this);
|
||||
inputPanel.setListener(this);
|
||||
|
@ -2168,16 +2170,16 @@ public class ConversationParentFragment extends Fragment
|
|||
attachButton.setOnLongClickListener(new AttachButtonLongClickListener());
|
||||
sendButton.setOnClickListener(sendButtonListener);
|
||||
sendButton.setEnabled(true);
|
||||
sendButton.addOnTransportChangedListener((newTransport, manuallySelected) -> {
|
||||
sendButton.addOnSelectionChangedListener((newMessageSendType, manuallySelected) -> {
|
||||
calculateCharactersRemaining();
|
||||
updateLinkPreviewState();
|
||||
linkPreviewViewModel.onTransportChanged(newTransport.isSms());
|
||||
composeText.setTransport(newTransport);
|
||||
linkPreviewViewModel.onTransportChanged(newMessageSendType.usesSmsTransport());
|
||||
composeText.setMessageSendType(newMessageSendType);
|
||||
|
||||
buttonToggle.getBackground().setColorFilter(getButtonToggleBackgroundColor(newTransport), PorterDuff.Mode.MULTIPLY);
|
||||
buttonToggle.getBackground().setColorFilter(getButtonToggleBackgroundColor(newMessageSendType), PorterDuff.Mode.MULTIPLY);
|
||||
buttonToggle.getBackground().invalidateSelf();
|
||||
|
||||
if (manuallySelected) recordTransportPreference(newTransport);
|
||||
if (manuallySelected) recordTransportPreference(newMessageSendType);
|
||||
});
|
||||
|
||||
titleView.setOnStoryRingClickListener(v -> handleStoryRingClick());
|
||||
|
@ -2222,13 +2224,13 @@ public class ConversationParentFragment extends Fragment
|
|||
material3OnScrollHelper = new Material3OnScrollHelper(Collections.singletonList(toolbarBackground), Collections.emptyList(), this::updateStatusBarColor);
|
||||
}
|
||||
|
||||
private @ColorInt int getButtonToggleBackgroundColor(TransportOption newTransport) {
|
||||
if (newTransport.isSms()) {
|
||||
return newTransport.getBackgroundColor();
|
||||
private @ColorInt int getButtonToggleBackgroundColor(MessageSendType newTransport) {
|
||||
if (newTransport.usesSmsTransport()) {
|
||||
return getResources().getColor(newTransport.getBackgroundColorRes());
|
||||
} else if (recipient != null) {
|
||||
return getRecipient().getChatColors().asSingleColor();
|
||||
} else {
|
||||
return newTransport.getBackgroundColor();
|
||||
return getResources().getColor(newTransport.getBackgroundColorRes());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2728,7 +2730,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
Media media = new Media(uri, mimeType, 0, width, height, 0, 0, borderless, videoGif, Optional.empty(), Optional.empty(), Optional.empty());
|
||||
startActivityForResult(MediaSelectionActivity.editor(requireContext(), sendButton.getSelectedTransport(), Collections.singletonList(media), recipient.getId(), composeText.getTextTrimmed()), MEDIA_SENDER);
|
||||
startActivityForResult(MediaSelectionActivity.editor(requireContext(), sendButton.getSelectedSendType(), Collections.singletonList(media), recipient.getId(), composeText.getTextTrimmed()), MEDIA_SENDER);
|
||||
return new SettableFuture<>(false);
|
||||
} else {
|
||||
return attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints(), width, height);
|
||||
|
@ -2749,7 +2751,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private void sendSharedContact(List<Contact> contacts) {
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1);
|
||||
int subscriptionId = sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1);
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds());
|
||||
boolean initiating = threadId == -1;
|
||||
|
||||
|
@ -2909,8 +2911,8 @@ public class ConversationParentFragment extends Fragment
|
|||
|
||||
private void calculateCharactersRemaining() {
|
||||
String messageBody = composeText.getTextTrimmed().toString();
|
||||
TransportOption transportOption = sendButton.getSelectedTransport();
|
||||
CharacterState characterState = transportOption.calculateCharacters(messageBody);
|
||||
MessageSendType sendType = sendButton.getSelectedSendType();
|
||||
CharacterState characterState = sendType.calculateCharacters(messageBody);
|
||||
|
||||
if (characterState.charactersRemaining <= 15 || characterState.messagesSpent > 1) {
|
||||
charactersLeft.setText(String.format(Locale.getDefault(),
|
||||
|
@ -2968,7 +2970,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private boolean isSmsForced() {
|
||||
return sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
|
||||
return sendButton.isManualSelection() && sendButton.getSelectedSendType().usesSmsTransport();
|
||||
}
|
||||
|
||||
protected Recipient getRecipient() {
|
||||
|
@ -2989,9 +2991,9 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private MediaConstraints getCurrentMediaConstraints() {
|
||||
return sendButton.getSelectedTransport().getType() == Type.TEXTSECURE
|
||||
return sendButton.getSelectedSendType().usesSignalTransport()
|
||||
? MediaConstraints.getPushMediaConstraints()
|
||||
: MediaConstraints.getMmsMediaConstraints(sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1));
|
||||
: MediaConstraints.getMmsMediaConstraints(sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1));
|
||||
}
|
||||
|
||||
private void markLastSeen() {
|
||||
|
@ -3050,12 +3052,12 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
String message = getMessage();
|
||||
TransportOption transport = sendButton.getSelectedTransport();
|
||||
boolean forceSms = (recipient.isForceSmsSelection() || sendButton.isManualSelection()) && transport.isSms();
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1);
|
||||
MessageSendType sendType = sendButton.getSelectedSendType();
|
||||
boolean forceSms = (recipient.isForceSmsSelection() || sendButton.isManualSelection()) && sendType.usesSmsTransport();
|
||||
int subscriptionId = sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1);
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
||||
boolean initiating = threadId == -1;
|
||||
boolean needsSplit = !transport.isSms() && message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
|
||||
boolean needsSplit = !sendType.usesSmsTransport() && message.length() > sendType.calculateCharacters(message).maxPrimaryMessageSize;
|
||||
boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
|
||||
recipient.isGroup() ||
|
||||
recipient.getEmail().isPresent() ||
|
||||
|
@ -3160,7 +3162,7 @@ public class ConversationParentFragment extends Fragment
|
|||
final long thread = this.threadId;
|
||||
|
||||
if (sendPush) {
|
||||
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(requireContext(), body, sendButton.getSelectedTransport().calculateCharacters(body).maxPrimaryMessageSize);
|
||||
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(requireContext(), body, sendButton.getSelectedSendType().calculateCharacters(body).maxPrimaryMessageSize);
|
||||
body = splitMessage.getBody();
|
||||
|
||||
if (splitMessage.getTextSlide().isPresent()) {
|
||||
|
@ -3294,7 +3296,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private void updateLinkPreviewState() {
|
||||
if (SignalStore.settings().isLinkPreviewsEnabled() && isSecureText && !sendButton.getSelectedTransport().isSms() && !attachmentManager.isAttachmentPresent() && getContext() != null) {
|
||||
if (SignalStore.settings().isLinkPreviewsEnabled() && isSecureText && !sendButton.getSelectedSendType().usesSmsTransport() && !attachmentManager.isAttachmentPresent() && getContext() != null) {
|
||||
linkPreviewViewModel.onEnabled();
|
||||
linkPreviewViewModel.onTextChanged(requireContext(), composeText.getTextTrimmed().toString(), composeText.getSelectionStart(), composeText.getSelectionEnd());
|
||||
} else {
|
||||
|
@ -3302,16 +3304,16 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
}
|
||||
|
||||
private void recordTransportPreference(TransportOption transportOption) {
|
||||
private void recordTransportPreference(MessageSendType sendType) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
|
||||
|
||||
recipientDatabase.setDefaultSubscriptionId(recipient.getId(), transportOption.getSimSubscriptionId().orElse(-1));
|
||||
recipientDatabase.setDefaultSubscriptionId(recipient.getId(), sendType.getSimSubscriptionIdOr(-1));
|
||||
|
||||
if (!recipient.resolve().isPushGroup()) {
|
||||
recipientDatabase.setForceSmsSelection(recipient.getId(), recipient.get().getRegistered() == RegisteredState.REGISTERED && transportOption.isSms());
|
||||
recipientDatabase.setForceSmsSelection(recipient.getId(), recipient.get().getRegistered() == RegisteredState.REGISTERED && sendType.usesSmsTransport());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -3446,9 +3448,9 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private void sendVoiceNote(@NonNull Uri uri, long size) {
|
||||
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
|
||||
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedSendType().usesSmsTransport();
|
||||
boolean initiating = threadId == -1;
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1);
|
||||
int subscriptionId = sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1);
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds());
|
||||
AudioSlide audioSlide = new AudioSlide(requireContext(), uri, size, MediaUtil.AUDIO_AAC, true);
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
|
@ -3487,23 +3489,23 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull String contentType, @NonNull Uri uri, long size, boolean clearCompose) {
|
||||
if (sendButton.getSelectedTransport().isSms()) {
|
||||
if (sendButton.getSelectedSendType().usesSmsTransport()) {
|
||||
Media media = new Media(uri, contentType, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, 0, false, false, Optional.empty(), Optional.empty(), Optional.empty());
|
||||
Intent intent = MediaSelectionActivity.editor(requireContext(), sendButton.getSelectedTransport(), Collections.singletonList(media), recipient.getId(), composeText.getTextTrimmed());
|
||||
Intent intent = MediaSelectionActivity.editor(requireContext(), sendButton.getSelectedSendType(), Collections.singletonList(media), recipient.getId(), composeText.getTextTrimmed());
|
||||
startActivityForResult(intent, MEDIA_SENDER);
|
||||
return;
|
||||
}
|
||||
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds());
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1);
|
||||
int subscriptionId = sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1);
|
||||
boolean initiating = threadId == -1;
|
||||
TransportOption transport = sendButton.getSelectedTransport();
|
||||
MessageSendType sendType = sendButton.getSelectedSendType();
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
Slide stickerSlide = new StickerSlide(requireContext(), uri, size, stickerLocator, contentType);
|
||||
|
||||
slideDeck.addSlide(stickerSlide);
|
||||
|
||||
sendMediaMessage(recipient.getId(), transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, clearCompose, null);
|
||||
sendMediaMessage(recipient.getId(), sendType.usesSmsTransport(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, clearCompose, null);
|
||||
}
|
||||
|
||||
private void silentlySetComposeText(String text) {
|
||||
|
@ -3558,7 +3560,7 @@ public class ConversationParentFragment extends Fragment
|
|||
|
||||
@Override
|
||||
public void openGifSearch() {
|
||||
AttachmentManager.selectGif(this, ConversationParentFragment.PICK_GIF, recipient.getId(), sendButton.getSelectedTransport(), isMms(), composeText.getTextTrimmed());
|
||||
AttachmentManager.selectGif(this, ConversationParentFragment.PICK_GIF, recipient.getId(), sendButton.getSelectedSendType(), isMms(), composeText.getTextTrimmed());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3651,7 +3653,7 @@ public class ConversationParentFragment extends Fragment
|
|||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
||||
.onAllGranted(() -> {
|
||||
composeText.clearFocus();
|
||||
startActivityForResult(MediaSelectionActivity.camera(requireActivity(), sendButton.getSelectedTransport(), recipient.getId(), inputPanel.getQuote().isPresent()), MEDIA_SENDER);
|
||||
startActivityForResult(MediaSelectionActivity.camera(requireActivity(), sendButton.getSelectedSendType(), recipient.getId(), inputPanel.getQuote().isPresent()), MEDIA_SENDER);
|
||||
requireActivity().overridePendingTransition(R.anim.camera_slide_from_bottom, R.anim.stationary);
|
||||
})
|
||||
.onAnyDenied(() -> Toast.makeText(requireContext(), R.string.ConversationActivity_signal_needs_camera_permissions_to_take_photos_or_video, Toast.LENGTH_LONG).show())
|
||||
|
@ -4146,7 +4148,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds());
|
||||
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().orElse(-1);
|
||||
int subscriptionId = sendButton.getSelectedSendType().getSimSubscriptionIdOr(-1);
|
||||
boolean initiating = threadId == -1;
|
||||
SlideDeck slideDeck = new SlideDeck();
|
||||
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
package org.thoughtcrime.securesms.conversation
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
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 java.lang.IllegalArgumentException
|
||||
|
||||
/**
|
||||
* The kinds of messages you can send, e.g. a plain Signal message, an SMS message, etc.
|
||||
*/
|
||||
@Parcelize
|
||||
sealed class MessageSendType(
|
||||
@StringRes
|
||||
val titleRes: Int,
|
||||
@StringRes
|
||||
val composeHintRes: Int,
|
||||
@DrawableRes
|
||||
val buttonDrawableRes: Int,
|
||||
@DrawableRes
|
||||
val menuDrawableRes: Int,
|
||||
@ColorRes
|
||||
val backgroundColorRes: Int,
|
||||
val transportType: TransportType,
|
||||
val characterCalculator: CharacterCalculator,
|
||||
open val simName: CharSequence? = null,
|
||||
open val simSubscriptionId: Int? = null
|
||||
) : Parcelable {
|
||||
|
||||
@get:JvmName("usesSmsTransport")
|
||||
val usesSmsTransport
|
||||
get() = transportType == TransportType.SMS
|
||||
|
||||
@get:JvmName("usesSignalTransport")
|
||||
val usesSignalTransport
|
||||
get() = transportType == TransportType.SIGNAL
|
||||
|
||||
fun calculateCharacters(body: String): CharacterCalculator.CharacterState {
|
||||
return characterCalculator.calculateCharacters(body)
|
||||
}
|
||||
|
||||
fun getSimSubscriptionIdOr(fallback: Int): Int {
|
||||
return simSubscriptionId ?: fallback
|
||||
}
|
||||
|
||||
open fun getTitle(context: Context): String {
|
||||
return context.getString(titleRes)
|
||||
}
|
||||
|
||||
/**
|
||||
* A type representing an SMS message, with optional SIM fields for multi-SIM devices.
|
||||
*/
|
||||
@Parcelize
|
||||
data class SmsMessageSendType(override val simName: CharSequence? = null, override val simSubscriptionId: Int? = null) : MessageSendType(
|
||||
titleRes = R.string.ConversationActivity_transport_insecure_sms,
|
||||
composeHintRes = R.string.conversation_activity__type_message_sms_insecure,
|
||||
buttonDrawableRes = R.drawable.ic_send_unlock_24,
|
||||
menuDrawableRes = R.drawable.ic_insecure_24,
|
||||
backgroundColorRes = R.color.core_grey_50,
|
||||
transportType = TransportType.SMS,
|
||||
characterCalculator = SmsCharacterCalculator(),
|
||||
simName = simName,
|
||||
simSubscriptionId = simSubscriptionId
|
||||
) {
|
||||
override fun getTitle(context: Context): String {
|
||||
return if (simName == null) {
|
||||
super.getTitle(context)
|
||||
} else {
|
||||
context.getString(R.string.ConversationActivity_transport_insecure_sms_with_sim, simName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type representing an MMS message, with optional SIM fields for multi-SIM devices.
|
||||
*/
|
||||
@Parcelize
|
||||
data class MmsMessageSendType(override val simName: CharSequence? = null, override val simSubscriptionId: Int? = null) : MessageSendType(
|
||||
titleRes = R.string.ConversationActivity_transport_insecure_mms,
|
||||
composeHintRes = R.string.conversation_activity__type_message_mms_insecure,
|
||||
buttonDrawableRes = R.drawable.ic_send_unlock_24,
|
||||
menuDrawableRes = R.drawable.ic_insecure_24,
|
||||
backgroundColorRes = R.color.core_grey_50,
|
||||
transportType = TransportType.SMS,
|
||||
characterCalculator = MmsCharacterCalculator(),
|
||||
simName = simName,
|
||||
simSubscriptionId = simSubscriptionId
|
||||
) {
|
||||
override fun getTitle(context: Context): String {
|
||||
return if (simName == null) {
|
||||
super.getTitle(context)
|
||||
} else {
|
||||
context.getString(R.string.ConversationActivity_transport_insecure_sms_with_sim, simName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type representing a basic Signal message.
|
||||
*/
|
||||
@Parcelize
|
||||
object SignalMessageSendType : MessageSendType(
|
||||
titleRes = R.string.ConversationActivity_transport_signal,
|
||||
composeHintRes = R.string.conversation_activity__type_message_push,
|
||||
buttonDrawableRes = R.drawable.ic_send_lock_24,
|
||||
menuDrawableRes = R.drawable.ic_secure_24,
|
||||
backgroundColorRes = R.color.core_ultramarine,
|
||||
transportType = TransportType.SIGNAL,
|
||||
characterCalculator = PushCharacterCalculator()
|
||||
)
|
||||
|
||||
enum class TransportType {
|
||||
SIGNAL, SMS
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns a list of all available [MessageSendType]s. Requires [Manifest.permission.READ_PHONE_STATE] in order to get available
|
||||
* SMS options.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getAllAvailable(context: Context, isMedia: Boolean = false): List<MessageSendType> {
|
||||
val options: MutableList<MessageSendType> = mutableListOf()
|
||||
|
||||
options += SignalMessageSendType
|
||||
|
||||
if (!Permissions.hasAll(context, Manifest.permission.READ_PHONE_STATE)) {
|
||||
return options
|
||||
}
|
||||
|
||||
val subscriptions: Collection<SubscriptionInfoCompat> = SubscriptionManagerCompat(context).activeAndReadySubscriptionInfos
|
||||
|
||||
if (subscriptions.size < 2) {
|
||||
options += if (isMedia) MmsMessageSendType() else SmsMessageSendType()
|
||||
} else {
|
||||
options += subscriptions.map {
|
||||
if (isMedia) {
|
||||
MmsMessageSendType(simName = it.displayName, simSubscriptionId = it.subscriptionId)
|
||||
} else {
|
||||
SmsMessageSendType(simName = it.displayName, simSubscriptionId = it.subscriptionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getFirstForTransport(context: Context, isMedia: Boolean, transportType: TransportType): MessageSendType {
|
||||
return getAllAvailable(context, isMedia).firstOrNull { it.transportType == transportType } ?: throw IllegalArgumentException("No options available for desired type $transportType!")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,9 @@ import android.content.Context
|
|||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.TransportOptions
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints
|
||||
|
@ -84,10 +83,9 @@ object Multiselect {
|
|||
return false
|
||||
}
|
||||
|
||||
val options = TransportOptions(context, true)
|
||||
options.setDefaultTransport(TransportOption.Type.SMS)
|
||||
val sendType: MessageSendType = MessageSendType.getFirstForTransport(context, true, MessageSendType.TransportType.SMS)
|
||||
|
||||
val mmsConstraints = MediaConstraints.getMmsMediaConstraints(options.selectedTransport.simSubscriptionId.orElse(-1))
|
||||
val mmsConstraints = MediaConstraints.getMmsMediaConstraints(sendType.simSubscriptionId ?: -1)
|
||||
return mmsConstraints.isSatisfied(context, mediaUri, mediaType, mediaSize) || mmsConstraints.canResize(mediaType)
|
||||
}
|
||||
|
||||
|
@ -108,10 +106,9 @@ object Multiselect {
|
|||
return false
|
||||
}
|
||||
|
||||
val options = TransportOptions(context, true)
|
||||
options.setDefaultTransport(TransportOption.Type.SMS)
|
||||
val sendType: MessageSendType = MessageSendType.getFirstForTransport(context, true, MessageSendType.TransportType.SMS)
|
||||
|
||||
val mmsConstraints = MediaConstraints.getMmsMediaConstraints(options.selectedTransport.simSubscriptionId.orElse(-1))
|
||||
val mmsConstraints = MediaConstraints.getMmsMediaConstraints(sendType.simSubscriptionId ?: -1)
|
||||
return mmsConstraints.isSatisfied(context, attachment) || mmsConstraints.canResize(attachment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import androidx.lifecycle.ViewModelProviders;
|
|||
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType;
|
||||
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4Fragment;
|
||||
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4SaveResult;
|
||||
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ViewModel;
|
||||
|
@ -47,7 +47,7 @@ public class GiphyActivity extends PassphraseRequiredActivity implements Keyboar
|
|||
private GiphyMp4ViewModel giphyMp4ViewModel;
|
||||
private AlertDialog progressDialog;
|
||||
private RecipientId recipientId;
|
||||
private TransportOption transport;
|
||||
private MessageSendType sendType;
|
||||
private CharSequence text;
|
||||
|
||||
@Override
|
||||
|
@ -62,7 +62,7 @@ public class GiphyActivity extends PassphraseRequiredActivity implements Keyboar
|
|||
final boolean forMms = getIntent().getBooleanExtra(EXTRA_IS_MMS, false);
|
||||
|
||||
recipientId = getIntent().getParcelableExtra(EXTRA_RECIPIENT_ID);
|
||||
transport = getIntent().getParcelableExtra(EXTRA_TRANSPORT);
|
||||
sendType = getIntent().getParcelableExtra(EXTRA_TRANSPORT);
|
||||
text = getIntent().getCharSequenceExtra(EXTRA_TEXT);
|
||||
|
||||
giphyMp4ViewModel = ViewModelProviders.of(this, new GiphyMp4ViewModel.Factory(forMms)).get(GiphyMp4ViewModel.class);
|
||||
|
@ -121,7 +121,7 @@ public class GiphyActivity extends PassphraseRequiredActivity implements Keyboar
|
|||
}
|
||||
|
||||
Media media = new Media(success.getBlobUri(), mimeType, 0, success.getWidth(), success.getHeight(), 0, 0, false, true, Optional.empty(), Optional.empty(), Optional.empty());
|
||||
startActivityForResult(MediaSelectionActivity.editor(this, transport, Collections.singletonList(media), recipientId, text), MEDIA_SENDER);
|
||||
startActivityForResult(MediaSelectionActivity.editor(this, sendType, Collections.singletonList(media), recipientId, text), MEDIA_SENDER);
|
||||
}
|
||||
|
||||
private void handleGiphyMp4ErrorResult(@NonNull GiphyMp4SaveResult.Error error) {
|
||||
|
|
|
@ -6,8 +6,8 @@ import android.os.Parcelable;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.database.model.StoryType;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
@ -30,7 +30,7 @@ public class MediaSendActivityResult implements Parcelable {
|
|||
private final Collection<PreUploadResult> uploadResults;
|
||||
private final Collection<Media> nonUploadedMedia;
|
||||
private final String body;
|
||||
private final TransportOption transport;
|
||||
private final MessageSendType sendType;
|
||||
private final boolean viewOnce;
|
||||
private final Collection<Mention> mentions;
|
||||
private final StoryType storyType;
|
||||
|
@ -47,32 +47,32 @@ public class MediaSendActivityResult implements Parcelable {
|
|||
public static @NonNull MediaSendActivityResult forPreUpload(@NonNull RecipientId recipientId,
|
||||
@NonNull Collection<PreUploadResult> uploadResults,
|
||||
@NonNull String body,
|
||||
@NonNull TransportOption transport,
|
||||
@NonNull MessageSendType sendType,
|
||||
boolean viewOnce,
|
||||
@NonNull List<Mention> mentions,
|
||||
@NonNull StoryType storyType)
|
||||
{
|
||||
Preconditions.checkArgument(uploadResults.size() > 0, "Must supply uploadResults!");
|
||||
return new MediaSendActivityResult(recipientId, uploadResults, Collections.emptyList(), body, transport, viewOnce, mentions, storyType);
|
||||
return new MediaSendActivityResult(recipientId, uploadResults, Collections.emptyList(), body, sendType, viewOnce, mentions, storyType);
|
||||
}
|
||||
|
||||
public static @NonNull MediaSendActivityResult forTraditionalSend(@NonNull RecipientId recipientId,
|
||||
@NonNull List<Media> nonUploadedMedia,
|
||||
@NonNull String body,
|
||||
@NonNull TransportOption transport,
|
||||
@NonNull MessageSendType sendType,
|
||||
boolean viewOnce,
|
||||
@NonNull List<Mention> mentions,
|
||||
@NonNull StoryType storyType)
|
||||
{
|
||||
Preconditions.checkArgument(nonUploadedMedia.size() > 0, "Must supply media!");
|
||||
return new MediaSendActivityResult(recipientId, Collections.emptyList(), nonUploadedMedia, body, transport, viewOnce, mentions, storyType);
|
||||
return new MediaSendActivityResult(recipientId, Collections.emptyList(), nonUploadedMedia, body, sendType, viewOnce, mentions, storyType);
|
||||
}
|
||||
|
||||
private MediaSendActivityResult(@NonNull RecipientId recipientId,
|
||||
@NonNull Collection<PreUploadResult> uploadResults,
|
||||
@NonNull List<Media> nonUploadedMedia,
|
||||
@NonNull String body,
|
||||
@NonNull TransportOption transport,
|
||||
@NonNull MessageSendType sendType,
|
||||
boolean viewOnce,
|
||||
@NonNull List<Mention> mentions,
|
||||
@NonNull StoryType storyType)
|
||||
|
@ -81,7 +81,7 @@ public class MediaSendActivityResult implements Parcelable {
|
|||
this.uploadResults = uploadResults;
|
||||
this.nonUploadedMedia = nonUploadedMedia;
|
||||
this.body = body;
|
||||
this.transport = transport;
|
||||
this.sendType = sendType;
|
||||
this.viewOnce = viewOnce;
|
||||
this.mentions = mentions;
|
||||
this.storyType = storyType;
|
||||
|
@ -92,7 +92,7 @@ public class MediaSendActivityResult implements Parcelable {
|
|||
this.uploadResults = ParcelUtil.readParcelableCollection(in, PreUploadResult.class);
|
||||
this.nonUploadedMedia = ParcelUtil.readParcelableCollection(in, Media.class);
|
||||
this.body = in.readString();
|
||||
this.transport = in.readParcelable(TransportOption.class.getClassLoader());
|
||||
this.sendType = in.readParcelable(MessageSendType.class.getClassLoader());
|
||||
this.viewOnce = ParcelUtil.readBoolean(in);
|
||||
this.mentions = ParcelUtil.readParcelableCollection(in, Mention.class);
|
||||
this.storyType = StoryType.fromCode(in.readInt());
|
||||
|
@ -118,8 +118,8 @@ public class MediaSendActivityResult implements Parcelable {
|
|||
return body;
|
||||
}
|
||||
|
||||
public @NonNull TransportOption getTransport() {
|
||||
return transport;
|
||||
public @NonNull MessageSendType getMessageSendType() {
|
||||
return sendType;
|
||||
}
|
||||
|
||||
public boolean isViewOnce() {
|
||||
|
@ -157,7 +157,7 @@ public class MediaSendActivityResult implements Parcelable {
|
|||
ParcelUtil.writeParcelableCollection(dest, uploadResults);
|
||||
ParcelUtil.writeParcelableCollection(dest, nonUploadedMedia);
|
||||
dest.writeString(body);
|
||||
dest.writeParcelable(transport, 0);
|
||||
dest.writeParcelable(sendType, 0);
|
||||
ParcelUtil.writeBoolean(dest, viewOnce);
|
||||
ParcelUtil.writeParcelableCollection(dest, mentions);
|
||||
dest.writeInt(storyType.getCode());
|
||||
|
|
|
@ -23,12 +23,11 @@ import org.signal.core.util.BreakIteratorCompat
|
|||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.TransportOptions
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFullScreenDialogFragment
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.SearchConfigurationProvider
|
||||
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog
|
||||
|
@ -87,12 +86,12 @@ class MediaSelectionActivity :
|
|||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
setContentView(R.layout.media_selection_activity)
|
||||
|
||||
val transportOption: TransportOption = requireNotNull(intent.getParcelableExtra(TRANSPORT_OPTION))
|
||||
val sendType: MessageSendType = requireNotNull(intent.getParcelableExtra(MESSAGE_SEND_TYPE))
|
||||
val initialMedia: List<Media> = intent.getParcelableArrayListExtra(MEDIA) ?: listOf()
|
||||
val message: CharSequence? = if (shareToTextStory) null else draftText
|
||||
val isReply: Boolean = intent.getBooleanExtra(IS_REPLY, false)
|
||||
|
||||
val factory = MediaSelectionViewModel.Factory(destination, transportOption, initialMedia, message, isReply, isStory, MediaSelectionRepository(this))
|
||||
val factory = MediaSelectionViewModel.Factory(destination, sendType, initialMedia, message, isReply, isStory, MediaSelectionRepository(this))
|
||||
viewModel = ViewModelProvider(this, factory)[MediaSelectionViewModel::class.java]
|
||||
|
||||
val textStoryToggle: ConstraintLayout = findViewById(R.id.switch_widget)
|
||||
|
@ -346,7 +345,7 @@ class MediaSelectionActivity :
|
|||
private const val NAV_HOST_TAG = "NAV_HOST"
|
||||
|
||||
private const val START_ACTION = "start.action"
|
||||
private const val TRANSPORT_OPTION = "transport.option"
|
||||
private const val MESSAGE_SEND_TYPE = "message.send.type"
|
||||
private const val MEDIA = "media"
|
||||
private const val MESSAGE = "message"
|
||||
private const val DESTINATION = "destination"
|
||||
|
@ -371,14 +370,14 @@ class MediaSelectionActivity :
|
|||
@JvmStatic
|
||||
fun camera(
|
||||
context: Context,
|
||||
transportOption: TransportOption,
|
||||
messageSendType: MessageSendType,
|
||||
recipientId: RecipientId,
|
||||
isReply: Boolean
|
||||
): Intent {
|
||||
return buildIntent(
|
||||
context = context,
|
||||
startAction = R.id.action_directly_to_mediaCaptureFragment,
|
||||
transportOption = transportOption,
|
||||
messageSendType = messageSendType,
|
||||
destination = MediaSelectionDestination.SingleRecipient(recipientId),
|
||||
isReply = isReply
|
||||
)
|
||||
|
@ -387,7 +386,7 @@ class MediaSelectionActivity :
|
|||
@JvmStatic
|
||||
fun gallery(
|
||||
context: Context,
|
||||
transportOption: TransportOption,
|
||||
messageSendType: MessageSendType,
|
||||
media: List<Media>,
|
||||
recipientId: RecipientId,
|
||||
message: CharSequence?,
|
||||
|
@ -396,7 +395,7 @@ class MediaSelectionActivity :
|
|||
return buildIntent(
|
||||
context = context,
|
||||
startAction = R.id.action_directly_to_mediaGalleryFragment,
|
||||
transportOption = transportOption,
|
||||
messageSendType = messageSendType,
|
||||
media = media,
|
||||
destination = MediaSelectionDestination.SingleRecipient(recipientId),
|
||||
message = message,
|
||||
|
@ -407,14 +406,14 @@ class MediaSelectionActivity :
|
|||
@JvmStatic
|
||||
fun editor(
|
||||
context: Context,
|
||||
transportOption: TransportOption,
|
||||
messageSendType: MessageSendType,
|
||||
media: List<Media>,
|
||||
recipientId: RecipientId,
|
||||
message: CharSequence?
|
||||
): Intent {
|
||||
return buildIntent(
|
||||
context = context,
|
||||
transportOption = transportOption,
|
||||
messageSendType = messageSendType,
|
||||
media = media,
|
||||
destination = MediaSelectionDestination.SingleRecipient(recipientId),
|
||||
message = message
|
||||
|
@ -424,7 +423,7 @@ class MediaSelectionActivity :
|
|||
@JvmStatic
|
||||
fun share(
|
||||
context: Context,
|
||||
transportOption: TransportOption,
|
||||
messageSendType: MessageSendType,
|
||||
media: List<Media>,
|
||||
recipientSearchKeys: List<ContactSearchKey.RecipientSearchKey>,
|
||||
message: CharSequence?,
|
||||
|
@ -432,7 +431,7 @@ class MediaSelectionActivity :
|
|||
): Intent {
|
||||
return buildIntent(
|
||||
context = context,
|
||||
transportOption = transportOption,
|
||||
messageSendType = messageSendType,
|
||||
media = media,
|
||||
destination = MediaSelectionDestination.MultipleRecipients(recipientSearchKeys),
|
||||
message = message,
|
||||
|
@ -444,7 +443,7 @@ class MediaSelectionActivity :
|
|||
private fun buildIntent(
|
||||
context: Context,
|
||||
startAction: Int = -1,
|
||||
transportOption: TransportOption = TransportOptions.getPushTransportOption(context),
|
||||
messageSendType: MessageSendType = MessageSendType.SignalMessageSendType,
|
||||
media: List<Media> = listOf(),
|
||||
destination: MediaSelectionDestination = MediaSelectionDestination.ChooseAfterMediaSelection,
|
||||
message: CharSequence? = null,
|
||||
|
@ -454,7 +453,7 @@ class MediaSelectionActivity :
|
|||
): Intent {
|
||||
return Intent(context, MediaSelectionActivity::class.java).apply {
|
||||
putExtra(START_ACTION, startAction)
|
||||
putExtra(TRANSPORT_OPTION, transportOption)
|
||||
putExtra(MESSAGE_SEND_TYPE, messageSendType)
|
||||
putParcelableArrayListExtra(MEDIA, ArrayList(media))
|
||||
putExtra(MESSAGE, message)
|
||||
putExtra(DESTINATION, destination.toBundle())
|
||||
|
|
|
@ -11,8 +11,8 @@ import org.signal.core.util.BreakIteratorCompat
|
|||
import org.signal.core.util.ThreadUtil
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.imageeditor.core.model.EditorModel
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase.TransformProperties
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
|
@ -76,7 +76,7 @@ class MediaSelectionRepository(context: Context) {
|
|||
singleContact: ContactSearchKey.RecipientSearchKey?,
|
||||
contacts: List<ContactSearchKey.RecipientSearchKey>,
|
||||
mentions: List<Mention>,
|
||||
transport: TransportOption
|
||||
sendType: MessageSendType
|
||||
): Maybe<MediaSendActivityResult> {
|
||||
if (isSms && contacts.isNotEmpty()) {
|
||||
throw IllegalStateException("Provided recipients to send to, but this is SMS!")
|
||||
|
@ -106,9 +106,9 @@ class MediaSelectionRepository(context: Context) {
|
|||
|
||||
if (isSms || MessageSender.isLocalSelfSend(context, singleRecipient, isSms)) {
|
||||
Log.i(TAG, "SMS or local self-send. Skipping pre-upload.")
|
||||
emitter.onSuccess(MediaSendActivityResult.forTraditionalSend(singleRecipient!!.id, updatedMedia, trimmedBody, transport, isViewOnce, trimmedMentions, StoryType.NONE))
|
||||
emitter.onSuccess(MediaSendActivityResult.forTraditionalSend(singleRecipient!!.id, updatedMedia, trimmedBody, sendType, isViewOnce, trimmedMentions, StoryType.NONE))
|
||||
} else {
|
||||
val splitMessage = MessageUtil.getSplitMessage(context, trimmedBody, transport.calculateCharacters(trimmedBody).maxPrimaryMessageSize)
|
||||
val splitMessage = MessageUtil.getSplitMessage(context, trimmedBody, sendType.calculateCharacters(trimmedBody).maxPrimaryMessageSize)
|
||||
val splitBody = splitMessage.body
|
||||
|
||||
if (splitMessage.textSlide.isPresent) {
|
||||
|
@ -135,10 +135,10 @@ class MediaSelectionRepository(context: Context) {
|
|||
uploadRepository.deleteAbandonedAttachments()
|
||||
emitter.onComplete()
|
||||
} else if (uploadResults.isNotEmpty()) {
|
||||
emitter.onSuccess(MediaSendActivityResult.forPreUpload(singleRecipient!!.id, uploadResults, splitBody, transport, isViewOnce, trimmedMentions, storyType))
|
||||
emitter.onSuccess(MediaSendActivityResult.forPreUpload(singleRecipient!!.id, uploadResults, splitBody, sendType, isViewOnce, trimmedMentions, storyType))
|
||||
} else {
|
||||
Log.w(TAG, "Got empty upload results! isSms: $isSms, updatedMedia.size(): ${updatedMedia.size}, isViewOnce: $isViewOnce, target: $singleContact")
|
||||
emitter.onSuccess(MediaSendActivityResult.forTraditionalSend(singleRecipient!!.id, updatedMedia, trimmedBody, transport, isViewOnce, trimmedMentions, storyType))
|
||||
emitter.onSuccess(MediaSendActivityResult.forTraditionalSend(singleRecipient!!.id, updatedMedia, trimmedBody, sendType, isViewOnce, trimmedMentions, storyType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.thoughtcrime.securesms.mediasend.v2
|
||||
|
||||
import android.net.Uri
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendConstants
|
||||
|
@ -9,7 +9,7 @@ import org.thoughtcrime.securesms.mms.SentMediaQuality
|
|||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
|
||||
data class MediaSelectionState(
|
||||
val transportOption: TransportOption,
|
||||
val sendType: MessageSendType,
|
||||
val selectedMedia: List<Media> = listOf(),
|
||||
val focusedMedia: Media? = null,
|
||||
val recipient: Recipient? = null,
|
||||
|
@ -25,7 +25,7 @@ data class MediaSelectionState(
|
|||
val isStory: Boolean
|
||||
) {
|
||||
|
||||
val maxSelection = if (transportOption.isSms) {
|
||||
val maxSelection = if (sendType.usesSmsTransport) {
|
||||
MediaSendConstants.MAX_SMS
|
||||
} else {
|
||||
MediaSendConstants.MAX_PUSH
|
||||
|
|
|
@ -10,9 +10,9 @@ import io.reactivex.rxjava3.core.Observable
|
|||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.components.mention.MentionAnnotation
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||
import org.thoughtcrime.securesms.mediasend.VideoEditorFragment
|
||||
|
@ -31,7 +31,7 @@ import java.util.Collections
|
|||
*/
|
||||
class MediaSelectionViewModel(
|
||||
val destination: MediaSelectionDestination,
|
||||
transportOption: TransportOption,
|
||||
sendType: MessageSendType,
|
||||
initialMedia: List<Media>,
|
||||
initialMessage: CharSequence?,
|
||||
val isReply: Boolean,
|
||||
|
@ -41,7 +41,7 @@ class MediaSelectionViewModel(
|
|||
|
||||
private val store: Store<MediaSelectionState> = Store(
|
||||
MediaSelectionState(
|
||||
transportOption = transportOption,
|
||||
sendType = sendType,
|
||||
message = initialMessage,
|
||||
isStory = isStory
|
||||
)
|
||||
|
@ -62,7 +62,7 @@ class MediaSelectionViewModel(
|
|||
store.update {
|
||||
it.copy(
|
||||
isMeteredConnection = metered,
|
||||
isPreUploadEnabled = shouldPreUpload(metered, it.transportOption.isSms, it.recipient)
|
||||
isPreUploadEnabled = shouldPreUpload(metered, it.sendType.usesSmsTransport, it.recipient)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class MediaSelectionViewModel(
|
|||
store.update(Recipient.live(recipientSearchKey.recipientId).liveData) { r, s ->
|
||||
s.copy(
|
||||
recipient = r,
|
||||
isPreUploadEnabled = shouldPreUpload(s.isMeteredConnection, s.transportOption.isSms, r)
|
||||
isPreUploadEnabled = shouldPreUpload(s.isMeteredConnection, s.sendType.usesSmsTransport, r)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -246,8 +246,8 @@ class MediaSelectionViewModel(
|
|||
}
|
||||
|
||||
fun getMediaConstraints(): MediaConstraints {
|
||||
return if (store.state.transportOption.isSms) {
|
||||
MediaConstraints.getMmsMediaConstraints(store.state.transportOption.simSubscriptionId.orElse(-1))
|
||||
return if (store.state.sendType.usesSmsTransport) {
|
||||
MediaConstraints.getMmsMediaConstraints(store.state.sendType.simSubscriptionId ?: -1)
|
||||
} else {
|
||||
MediaConstraints.getPushMediaConstraints()
|
||||
}
|
||||
|
@ -293,18 +293,18 @@ class MediaSelectionViewModel(
|
|||
store.state.editorStateMap,
|
||||
store.state.quality,
|
||||
store.state.message,
|
||||
store.state.transportOption.isSms,
|
||||
store.state.sendType.usesSmsTransport,
|
||||
isViewOnceEnabled(),
|
||||
destination.getRecipientSearchKey(),
|
||||
selectedContacts.ifEmpty { destination.getRecipientSearchKeyList() },
|
||||
MentionAnnotation.getMentionsFromAnnotations(store.state.message),
|
||||
store.state.transportOption
|
||||
store.state.sendType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun isViewOnceEnabled(): Boolean {
|
||||
return !store.state.transportOption.isSms &&
|
||||
return !store.state.sendType.usesSmsTransport &&
|
||||
store.state.selectedMedia.size == 1 &&
|
||||
store.state.viewOnceToggleState == MediaSelectionState.ViewOnceToggleState.ONCE
|
||||
}
|
||||
|
@ -427,7 +427,7 @@ class MediaSelectionViewModel(
|
|||
|
||||
class Factory(
|
||||
private val destination: MediaSelectionDestination,
|
||||
private val transportOption: TransportOption,
|
||||
private val sendType: MessageSendType,
|
||||
private val initialMedia: List<Media>,
|
||||
private val initialMessage: CharSequence?,
|
||||
private val isReply: Boolean,
|
||||
|
@ -435,7 +435,7 @@ class MediaSelectionViewModel(
|
|||
private val repository: MediaSelectionRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return requireNotNull(modelClass.cast(MediaSelectionViewModel(destination, transportOption, initialMedia, initialMessage, isReply, isStory, repository)))
|
||||
return requireNotNull(modelClass.cast(MediaSelectionViewModel(destination, sendType, initialMedia, initialMessage, isReply, isStory, repository)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ import androidx.viewpager2.widget.ViewPager2
|
|||
import app.cash.exhaustive.Exhaustive
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||
|
@ -200,7 +200,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
|
|||
state.selectedMedia.map { MediaReviewSelectedItem.Model(it, state.focusedMedia == it) } + MediaReviewAddItem.Model
|
||||
)
|
||||
|
||||
presentSendButton(state.transportOption)
|
||||
presentSendButton(state.sendType)
|
||||
presentPager(state)
|
||||
presentAddMessageEntry(state.message)
|
||||
presentImageQualityToggle(state.quality)
|
||||
|
@ -289,8 +289,8 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
|
|||
)
|
||||
}
|
||||
|
||||
private fun presentSendButton(transportOption: TransportOption) {
|
||||
val sendButtonTint = if (transportOption.type == TransportOption.Type.TEXTSECURE) {
|
||||
private fun presentSendButton(sendType: MessageSendType) {
|
||||
val sendButtonTint = if (sendType.usesSignalTransport) {
|
||||
R.color.core_ultramarine
|
||||
} else {
|
||||
R.color.core_grey_50
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.signal.core.util.ThreadUtil;
|
|||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.MediaPreviewActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.components.AudioView;
|
||||
import org.thoughtcrime.securesms.components.DocumentView;
|
||||
|
@ -50,6 +49,7 @@ import org.thoughtcrime.securesms.components.RemovableEditableMediaView;
|
|||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.components.location.SignalMapView;
|
||||
import org.thoughtcrime.securesms.components.location.SignalPlace;
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType;
|
||||
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
||||
import org.thoughtcrime.securesms.maps.PlacePickerActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity;
|
||||
|
@ -374,12 +374,12 @@ public class AttachmentManager {
|
|||
selectMediaType(fragment, "*/*", null, requestCode);
|
||||
}
|
||||
|
||||
public static void selectGallery(Fragment fragment, int requestCode, @NonNull Recipient recipient, @NonNull CharSequence body, @NonNull TransportOption transport, boolean hasQuote) {
|
||||
public static void selectGallery(Fragment fragment, int requestCode, @NonNull Recipient recipient, @NonNull CharSequence body, @NonNull MessageSendType messageSendType, boolean hasQuote) {
|
||||
Permissions.with(fragment)
|
||||
.request(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(fragment.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio))
|
||||
.onAllGranted(() -> fragment.startActivityForResult(MediaSelectionActivity.gallery(fragment.requireContext(), transport, Collections.emptyList(), recipient.getId(), body, hasQuote), requestCode))
|
||||
.onAllGranted(() -> fragment.startActivityForResult(MediaSelectionActivity.gallery(fragment.requireContext(), messageSendType, Collections.emptyList(), recipient.getId(), body, hasQuote), requestCode))
|
||||
.execute();
|
||||
}
|
||||
|
||||
|
@ -404,11 +404,11 @@ public class AttachmentManager {
|
|||
.execute();
|
||||
}
|
||||
|
||||
public static void selectGif(Fragment fragment, int requestCode, RecipientId id, TransportOption selectedTransport, boolean isForMms, CharSequence textTrimmed) {
|
||||
public static void selectGif(Fragment fragment, int requestCode, RecipientId id, MessageSendType sendType, boolean isForMms, CharSequence textTrimmed) {
|
||||
Intent intent = new Intent(fragment.requireContext(), GiphyActivity.class);
|
||||
intent.putExtra(GiphyActivity.EXTRA_IS_MMS, isForMms);
|
||||
intent.putExtra(GiphyActivity.EXTRA_RECIPIENT_ID, id);
|
||||
intent.putExtra(GiphyActivity.EXTRA_TRANSPORT, selectedTransport);
|
||||
intent.putExtra(GiphyActivity.EXTRA_TRANSPORT, sendType);
|
||||
intent.putExtra(GiphyActivity.EXTRA_TEXT, textTrimmed);
|
||||
fragment.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
|
|
@ -16,9 +16,8 @@ import org.signal.core.util.BreakIteratorCompat;
|
|||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.TransportOptions;
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey;
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
|
@ -98,13 +97,13 @@ public final class MultiShareSender {
|
|||
|
||||
long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
|
||||
List<Mention> mentions = getValidMentionsForRecipient(recipient, multiShareArgs.getMentions());
|
||||
TransportOption transport = resolveTransportOption(context, recipient);
|
||||
boolean forceSms = recipient.isForceSmsSelection() && transport.isSms();
|
||||
int subscriptionId = transport.getSimSubscriptionId().orElse(-1);
|
||||
MessageSendType sendType = resolveTransportOption(context, recipient);
|
||||
boolean forceSms = recipient.isForceSmsSelection() && sendType.usesSmsTransport();
|
||||
int subscriptionId = sendType.getSimSubscriptionIdOr(-1);
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds());
|
||||
boolean needsSplit = !transport.isSms() &&
|
||||
message != null &&
|
||||
message.length() > transport.calculateCharacters(message).maxPrimaryMessageSize;
|
||||
boolean needsSplit = !sendType.usesSmsTransport() &&
|
||||
message != null &&
|
||||
message.length() > sendType.calculateCharacters(message).maxPrimaryMessageSize;
|
||||
boolean hasMmsMedia = !multiShareArgs.getMedia().isEmpty() ||
|
||||
(multiShareArgs.getDataUri() != null && multiShareArgs.getDataUri() != Uri.EMPTY) ||
|
||||
multiShareArgs.getStickerLocator() != null ||
|
||||
|
@ -119,8 +118,8 @@ public final class MultiShareSender {
|
|||
|
||||
if ((recipient.isMmsGroup() || recipient.getEmail().isPresent()) && !isMmsEnabled) {
|
||||
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.MMS_NOT_ENABLED));
|
||||
} else if (hasMmsMedia && transport.isSms() || hasPushMedia && !transport.isSms() || canSendAsTextStory) {
|
||||
sendMediaMessageOrCollectStoryToBatch(context, multiShareArgs, recipient, slideDeck, transport, threadId, forceSms, expiresIn, multiShareArgs.isViewOnce(), subscriptionId, mentions, recipientSearchKey.isStory(), sentTimestamp, canSendAsTextStory, storiesBatch);
|
||||
} else if (hasMmsMedia && sendType.usesSmsTransport() || hasPushMedia && !sendType.usesSmsTransport() || canSendAsTextStory) {
|
||||
sendMediaMessageOrCollectStoryToBatch(context, multiShareArgs, recipient, slideDeck, sendType, threadId, forceSms, expiresIn, multiShareArgs.isViewOnce(), subscriptionId, mentions, recipientSearchKey.isStory(), sentTimestamp, canSendAsTextStory, storiesBatch);
|
||||
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.SUCCESS));
|
||||
} else if (recipientSearchKey.isStory()) {
|
||||
results.add(new MultiShareSendResult(recipientSearchKey, MultiShareSendResult.Type.INVALID_SHARE_TO_STORY));
|
||||
|
@ -146,28 +145,26 @@ public final class MultiShareSender {
|
|||
return new MultiShareSendResultCollection(results);
|
||||
}
|
||||
|
||||
public static @NonNull TransportOption getWorstTransportOption(@NonNull Context context, @NonNull Set<ContactSearchKey.RecipientSearchKey> recipientSearchKeys) {
|
||||
public static @NonNull MessageSendType getWorstTransportOption(@NonNull Context context, @NonNull Set<ContactSearchKey.RecipientSearchKey> recipientSearchKeys) {
|
||||
for (ContactSearchKey.RecipientSearchKey recipientSearchKey : recipientSearchKeys) {
|
||||
TransportOption option = resolveTransportOption(context, Recipient.resolved(recipientSearchKey.getRecipientId()).isForceSmsSelection() && !recipientSearchKey.isStory());
|
||||
if (option.isSms()) {
|
||||
return option;
|
||||
MessageSendType type = resolveTransportOption(context, Recipient.resolved(recipientSearchKey.getRecipientId()).isForceSmsSelection() && !recipientSearchKey.isStory());
|
||||
if (type.usesSmsTransport()) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return TransportOptions.getPushTransportOption(context);
|
||||
return MessageSendType.SignalMessageSendType.INSTANCE;
|
||||
}
|
||||
|
||||
private static @NonNull TransportOption resolveTransportOption(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
private static @NonNull MessageSendType resolveTransportOption(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
return resolveTransportOption(context, !recipient.isDistributionList() && (recipient.isForceSmsSelection() || !recipient.isRegistered()));
|
||||
}
|
||||
|
||||
public static @NonNull TransportOption resolveTransportOption(@NonNull Context context, boolean forceSms) {
|
||||
public static @NonNull MessageSendType resolveTransportOption(@NonNull Context context, boolean forceSms) {
|
||||
if (forceSms) {
|
||||
TransportOptions options = new TransportOptions(context, false);
|
||||
options.setDefaultTransport(TransportOption.Type.SMS);
|
||||
return options.getSelectedTransport();
|
||||
return MessageSendType.getFirstForTransport(context, false, MessageSendType.TransportType.SMS);
|
||||
} else {
|
||||
return TransportOptions.getPushTransportOption(context);
|
||||
return MessageSendType.SignalMessageSendType.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +172,7 @@ public final class MultiShareSender {
|
|||
@NonNull MultiShareArgs multiShareArgs,
|
||||
@NonNull Recipient recipient,
|
||||
@NonNull SlideDeck slideDeck,
|
||||
@NonNull TransportOption transportOption,
|
||||
@NonNull MessageSendType sendType,
|
||||
long threadId,
|
||||
boolean forceSms,
|
||||
long expiresIn,
|
||||
|
@ -188,8 +185,8 @@ public final class MultiShareSender {
|
|||
@NonNull List<OutgoingMediaMessage> storiesToBatchSend)
|
||||
{
|
||||
String body = multiShareArgs.getDraftText();
|
||||
if (transportOption.isType(TransportOption.Type.TEXTSECURE) && !forceSms && body != null) {
|
||||
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(context, body, transportOption.calculateCharacters(body).maxPrimaryMessageSize);
|
||||
if (sendType.usesSignalTransport() && !forceSms && body != null) {
|
||||
MessageUtil.SplitResult splitMessage = MessageUtil.getSplitMessage(context, body, sendType.calculateCharacters(body).maxPrimaryMessageSize);
|
||||
body = splitMessage.getBody();
|
||||
|
||||
if (splitMessage.getTextSlide().isPresent()) {
|
||||
|
|
|
@ -11,10 +11,9 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.core.util.toKotlinPair
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.TransportOption
|
||||
import org.thoughtcrime.securesms.TransportOptions
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.attachments.UriAttachment
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendConstants
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints
|
||||
|
@ -180,9 +179,8 @@ class ShareRepository(context: Context) {
|
|||
return false
|
||||
}
|
||||
|
||||
val options = TransportOptions(context, true)
|
||||
options.setDefaultTransport(TransportOption.Type.SMS)
|
||||
val mmsConstraints = MediaConstraints.getMmsMediaConstraints(options.selectedTransport.simSubscriptionId.orElse(-1))
|
||||
val sendType: MessageSendType = MessageSendType.getFirstForTransport(context, true, MessageSendType.TransportType.SMS)
|
||||
val mmsConstraints = MediaConstraints.getMmsMediaConstraints(sendType.simSubscriptionId ?: -1)
|
||||
return mmsConstraints.isSatisfied(context, attachment) || mmsConstraints.canResize(attachment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,35 +16,13 @@
|
|||
*/
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public abstract class CharacterCalculator {
|
||||
public abstract class CharacterCalculator implements Parcelable {
|
||||
|
||||
public abstract CharacterState calculateCharacters(String messageBody);
|
||||
|
||||
public static CharacterCalculator readFromParcel(@NonNull Parcel in) {
|
||||
switch (in.readInt()) {
|
||||
case 1: return new SmsCharacterCalculator();
|
||||
case 2: return new MmsCharacterCalculator();
|
||||
case 3: return new PushCharacterCalculator();
|
||||
default: throw new IllegalArgumentException("Read an unsupported value for a calculator.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeToParcel(@NonNull Parcel dest, @NonNull CharacterCalculator calculator) {
|
||||
if (calculator instanceof SmsCharacterCalculator) {
|
||||
dest.writeInt(1);
|
||||
} else if (calculator instanceof MmsCharacterCalculator) {
|
||||
dest.writeInt(2);
|
||||
} else if (calculator instanceof PushCharacterCalculator) {
|
||||
dest.writeInt(3);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Tried to write an unsupported calculator to a parcel.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class CharacterState {
|
||||
public final int charactersRemaining;
|
||||
public final int messagesSpent;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
public class MmsCharacterCalculator extends CharacterCalculator {
|
||||
|
||||
private static final int MAX_SIZE = 5000;
|
||||
|
@ -8,4 +10,25 @@ public class MmsCharacterCalculator extends CharacterCalculator {
|
|||
public CharacterState calculateCharacters(String messageBody) {
|
||||
return new CharacterState(1, MAX_SIZE - messageBody.length(), MAX_SIZE, MAX_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
}
|
||||
|
||||
public static final Creator<MmsCharacterCalculator> CREATOR = new Creator<MmsCharacterCalculator>() {
|
||||
@Override
|
||||
public MmsCharacterCalculator createFromParcel(Parcel in) {
|
||||
return new MmsCharacterCalculator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MmsCharacterCalculator[] newArray(int size) {
|
||||
return new MmsCharacterCalculator[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
public class PushCharacterCalculator extends CharacterCalculator {
|
||||
private static final int MAX_TOTAL_SIZE = 64 * 1024;
|
||||
private static final int MAX_PRIMARY_SIZE = 2000;
|
||||
|
@ -23,5 +25,26 @@ public class PushCharacterCalculator extends CharacterCalculator {
|
|||
public CharacterState calculateCharacters(String messageBody) {
|
||||
return new CharacterState(1, MAX_TOTAL_SIZE - messageBody.length(), MAX_TOTAL_SIZE, MAX_PRIMARY_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
}
|
||||
|
||||
public static final Creator<PushCharacterCalculator> CREATOR = new Creator<PushCharacterCalculator>() {
|
||||
@Override
|
||||
public PushCharacterCalculator createFromParcel(Parcel in) {
|
||||
return new PushCharacterCalculator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushCharacterCalculator[] newArray(int size) {
|
||||
return new PushCharacterCalculator[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
|
@ -62,4 +63,25 @@ public class SmsCharacterCalculator extends CharacterCalculator {
|
|||
|
||||
return new CharacterState(messagesSpent, charactersRemaining, maxMessageSize, maxMessageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
}
|
||||
|
||||
public static final Creator<SmsCharacterCalculator> CREATOR = new Creator<SmsCharacterCalculator>() {
|
||||
@Override
|
||||
public SmsCharacterCalculator createFromParcel(Parcel in) {
|
||||
return new SmsCharacterCalculator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsCharacterCalculator[] newArray(int size) {
|
||||
return new SmsCharacterCalculator[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
11
app/src/main/res/drawable/ic_insecure_24.xml
Normal file
11
app/src/main/res/drawable/ic_insecure_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2.833,22.691H13.161C14.72,22.691 15.494,21.917 15.494,20.214V12.308C15.494,10.87 14.93,10.096 13.802,9.897V7.133C13.802,4.247 15.638,2.544 17.971,2.544C20.304,2.544 22.129,4.247 22.129,7.133V9.554C22.129,10.052 22.438,10.317 22.826,10.317C23.19,10.317 23.5,10.074 23.5,9.554V7.265C23.5,3.373 20.968,1.25 17.971,1.25C14.964,1.25 12.431,3.373 12.431,7.265V9.853H2.833C1.274,9.853 0.5,10.616 0.5,12.308V20.214C0.5,21.917 1.274,22.691 2.833,22.691ZM2.866,21.408C2.192,21.408 1.871,21.132 1.871,20.302V12.23C1.871,11.39 2.192,11.125 2.866,11.125H13.139C13.814,11.125 14.123,11.39 14.123,12.23V20.302C14.123,21.132 13.814,21.408 13.139,21.408H2.866Z"
|
||||
android:strokeWidth="0.25"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
11
app/src/main/res/drawable/ic_secure_24.xml
Normal file
11
app/src/main/res/drawable/ic_secure_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6.694,23H17.291C18.891,23 19.685,22.206 19.685,20.458V12.346C19.685,10.746 19.016,9.963 17.666,9.839V7.172C17.666,3.178 15.067,1 11.993,1C8.918,1 6.32,3.178 6.32,7.172V9.839C4.969,9.963 4.3,10.746 4.3,12.346V20.458C4.3,22.206 5.094,23 6.694,23ZM7.715,7.036C7.715,4.075 9.587,2.327 11.993,2.327C14.387,2.327 16.27,4.075 16.27,7.036V9.827H7.715V7.036ZM6.728,21.684C6.036,21.684 5.707,21.4 5.707,20.549V12.267C5.707,11.404 6.036,11.132 6.728,11.132H17.268C17.961,11.132 18.278,11.404 18.278,12.267V20.549C18.278,21.4 17.961,21.684 17.268,21.684H6.728Z"
|
||||
android:strokeWidth="0.25"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"/>
|
||||
</vector>
|
|
@ -259,8 +259,12 @@
|
|||
<string name="ConversationActivity_calls_not_supported">Calls not supported</string>
|
||||
<string name="ConversationActivity_this_device_does_not_appear_to_support_dial_actions">This device does not appear to support dial actions.</string>
|
||||
<string name="ConversationActivity_transport_insecure_sms">Insecure SMS</string>
|
||||
<!-- A title for the option to send an SMS with a placeholder to put the name of their SIM card -->
|
||||
<string name="ConversationActivity_transport_insecure_sms_with_sim">Insecure SMS (%1$s)</string>
|
||||
<string name="ConversationActivity_transport_insecure_mms">Insecure MMS</string>
|
||||
<string name="ConversationActivity_transport_signal">Signal</string>
|
||||
<!-- A title for the option to send an SMS with a placeholder to put the name of their SIM card -->
|
||||
<string name="ConversationActivity_transport_insecure_mms_with_sim">Insecure MMS (%1$s)</string>
|
||||
<string name="ConversationActivity_transport_signal">Signal message</string>
|
||||
<string name="ConversationActivity_lets_switch_to_signal">Let\'s switch to Signal %1$s</string>
|
||||
<string name="ConversationActivity_specify_recipient">Please choose a contact</string>
|
||||
<string name="ConversationActivity_unblock">Unblock</string>
|
||||
|
|
|
@ -2,30 +2,22 @@ package org.signal.qr
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageFormat
|
||||
import android.util.Size
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.camera.core.AspectRatio
|
||||
import androidx.camera.core.Camera
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.math.MathUtils.clamp
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.qr.kitkat.ScanListener
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
||||
/**
|
||||
* API21+ version of QR scanning view. Uses camerax APIs.
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue