Add group profile sharing logic

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-08-22 11:51:01 -07:00
parent f17af19d09
commit 64ad9ec9dd
10 changed files with 164 additions and 23 deletions

View File

@ -23,6 +23,12 @@
android:clipToPadding="false" android:clipToPadding="false"
android:clipChildren="false"> android:clipChildren="false">
<ViewStub android:id="@+id/group_share_profile_view_stub"
android:layout="@layout/conversation_activity_group_share_profile_stub"
android:inflatedId="@+id/group_share_profile_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ViewStub android:id="@+id/unverified_banner_stub" <ViewStub android:id="@+id/unverified_banner_stub"
android:layout="@layout/conversation_activity_unverified_banner_stub" android:layout="@layout/conversation_activity_unverified_banner_stub"
android:inflatedId="@+id/unverified_banner" android:inflatedId="@+id/unverified_banner"

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.profiles.GroupShareProfileView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/group_share_profile_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/reminder_background"
android:focusable="true"
android:orientation="horizontal"
tools:visibility="visible">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_face_white_24dp"/>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="18sp"
android:layout_margin="16dp"
android:text="@string/profile_group_share_view__make_your_profile_name_and_photo_visible_to_this_group"/>
</LinearLayout>

View File

@ -327,6 +327,11 @@
<string name="GroupCreateActivity_loading_group_details">Loading group details...</string> <string name="GroupCreateActivity_loading_group_details">Loading group details...</string>
<string name="GroupCreateActivity_youre_already_in_the_group">You\'re already in the group.</string> <string name="GroupCreateActivity_youre_already_in_the_group">You\'re already in the group.</string>
<!-- GroupShareProfileView -->
<string name="GroupShareProfileView_share_your_profile_name_and_photo_with_this_group">Share your profile name and photo with this group?</string>
<string name="GroupShareProfileView_do_you_want_to_make_your_profile_name_and_photo_visible_to_all_current_and_future_members_of_this_group">Do you want to make your profile name and photo visible to all current and future members of this group?</string>
<string name="GroupShareProfileView_make_visible">Make visible</string>
<!-- GroupMembersDialog --> <!-- GroupMembersDialog -->
<string name="GroupMembersDialog_me">Me</string> <string name="GroupMembersDialog_me">Me</string>
@ -959,6 +964,9 @@
</plurals> </plurals>
<string name="GroupUtil_group_name_is_now">Group name is now \'%1$s\'.</string> <string name="GroupUtil_group_name_is_now">Group name is now \'%1$s\'.</string>
<!-- profile_group_share_view -->
<string name="profile_group_share_view__make_your_profile_name_and_photo_visible_to_this_group">Make your profile name and photo visible to this group?</string>
<!-- prompt_passphrase_activity --> <!-- prompt_passphrase_activity -->
<string name="prompt_passphrase_activity__unlock">Unlock</string> <string name="prompt_passphrase_activity__unlock">Unlock</string>

View File

@ -127,6 +127,7 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.profiles.GroupShareProfileView;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
@ -206,20 +207,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int PICK_GIF = 9; private static final int PICK_GIF = 9;
private static final int SMS_DEFAULT = 10; private static final int SMS_DEFAULT = 10;
private MasterSecret masterSecret; private MasterSecret masterSecret;
protected ComposeText composeText; protected ComposeText composeText;
private AnimatingToggle buttonToggle; private AnimatingToggle buttonToggle;
private SendButton sendButton; private SendButton sendButton;
private ImageButton attachButton; private ImageButton attachButton;
protected ConversationTitleView titleView; protected ConversationTitleView titleView;
private TextView charactersLeft; private TextView charactersLeft;
private ConversationFragment fragment; private ConversationFragment fragment;
private Button unblockButton; private Button unblockButton;
private Button makeDefaultSmsButton; private Button makeDefaultSmsButton;
private InputAwareLayout container; private InputAwareLayout container;
private View composePanel; private View composePanel;
protected Stub<ReminderView> reminderView; protected Stub<ReminderView> reminderView;
private Stub<UnverifiedBannerView> unverifiedBannerView; private Stub<UnverifiedBannerView> unverifiedBannerView;
private Stub<GroupShareProfileView> groupShareProfileView;
private AttachmentTypeSelector attachmentTypeSelector; private AttachmentTypeSelector attachmentTypeSelector;
private AttachmentManager attachmentManager; private AttachmentManager attachmentManager;
@ -323,6 +325,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
titleView.setTitle(recipient); titleView.setTitle(recipient);
setActionBarColor(recipient.getColor()); setActionBarColor(recipient.getColor());
setBlockedUserState(recipient, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
setGroupShareProfileReminder(recipient);
calculateCharactersRemaining(); calculateCharactersRemaining();
MessageNotifier.setVisibleThread(threadId); MessageNotifier.setVisibleThread(threadId);
@ -1149,6 +1152,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
container = ViewUtil.findById(this, R.id.layout_container); container = ViewUtil.findById(this, R.id.layout_container);
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub); reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub); unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub);
quickAttachmentDrawer = ViewUtil.findById(this, R.id.quick_attachment_drawer); quickAttachmentDrawer = ViewUtil.findById(this, R.id.quick_attachment_drawer);
quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle); quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
inputPanel = ViewUtil.findById(this, R.id.bottom_panel); inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
@ -1274,6 +1278,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
titleView.setVerified(identityRecords.isVerified()); titleView.setVerified(identityRecords.isVerified());
setBlockedUserState(recipient, isSecureText, isDefaultSms); setBlockedUserState(recipient, isSecureText, isDefaultSms);
setActionBarColor(recipient.getColor()); setActionBarColor(recipient.getColor());
setGroupShareProfileReminder(recipient);
updateInviteReminder(recipient.hasSeenInviteReminder()); updateInviteReminder(recipient.hasSeenInviteReminder());
updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId());
initializeSecurity(isSecureText, isDefaultSms); initializeSecurity(isSecureText, isDefaultSms);
@ -1460,6 +1465,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
} }
private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing()) {
groupShareProfileView.get().setRecipient(recipient);
groupShareProfileView.get().setVisibility(View.VISIBLE);
} else if (groupShareProfileView.resolved()) {
groupShareProfileView.get().setVisibility(View.GONE);
}
}
private void calculateCharactersRemaining() { private void calculateCharactersRemaining() {
String messageBody = composeText.getTextTrimmed(); String messageBody = composeText.getTextTrimmed();
TransportOption transportOption = sendButton.getSelectedTransport(); TransportOption transportOption = sendButton.getSelectedTransport();

View File

@ -56,6 +56,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.loaders.ConversationLoader; import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -430,7 +431,7 @@ public class ConversationFragment extends Fragment
setLastSeen(loader.getLastSeen()); setLastSeen(loader.getLastSeen());
} }
if (!loader.hasSent() && !recipient.isGroupRecipient() && recipient.getName() == null) { if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() != RecipientDatabase.RegisteredState.REGISTERED) {
getListAdapter().setHeaderView(unknownSenderView); getListAdapter().setHeaderView(unknownSenderView);
} else { } else {
getListAdapter().setHeaderView(null); getListAdapter().setHeaderView(null);

View File

@ -71,9 +71,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
initializeContactUpdatesReceiver(); initializeContactUpdatesReceiver();
Intent intent = new Intent(this, CreateProfileActivity.class);
startActivity(intent);
RatingManager.showRatingDialogIfNecessary(this); RatingManager.showRatingDialogIfNecessary(this);
} }

View File

@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -138,6 +139,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
{ {
SignalServiceMessageSender messageSender = messageSenderFactory.create(); SignalServiceMessageSender messageSender = messageSenderFactory.create();
String groupId = message.getRecipient().getAddress().toGroupString(); String groupId = message.getRecipient().getAddress().toGroupString();
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false); List<Recipient> recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
@ -154,15 +156,23 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
SignalServiceAttachment avatar = attachmentStreams.isEmpty() ? null : attachmentStreams.get(0); SignalServiceAttachment avatar = attachmentStreams.isEmpty() ? null : attachmentStreams.get(0);
SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), groupContext.getMembersList(), avatar); SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedId(groupId), groupContext.getName(), groupContext.getMembersList(), avatar);
SignalServiceDataMessage groupDataMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group, null, null); SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.asGroupMessage(group)
.build();
messageSender.sendMessage(addresses, groupDataMessage); messageSender.sendMessage(addresses, groupDataMessage);
} else { } else {
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId)); SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedId(groupId));
SignalServiceDataMessage groupMessage = new SignalServiceDataMessage(message.getSentTimeMillis(), group, SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder()
attachmentStreams, message.getBody(), false, .withTimestamp(message.getSentTimeMillis())
(int)(message.getExpiresIn() / 1000), .asGroupMessage(group)
message.isExpirationUpdate(), null); .withAttachments(attachmentStreams)
.withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000))
.asExpirationUpdate(message.isExpirationUpdate())
.withProfileKey(profileKey.orNull())
.build();
messageSender.sendMessage(addresses, groupMessage); messageSender.sendMessage(addresses, groupMessage);
} }

View File

@ -0,0 +1,69 @@
package org.thoughtcrime.securesms.profiles;
import android.content.Context;
import android.os.Build;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.annotation.StyleRes;
import android.support.v7.app.AlertDialog;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ViewUtil;
public class GroupShareProfileView extends FrameLayout {
private View container;
private @Nullable Recipient recipient;
public GroupShareProfileView(@NonNull Context context) {
super(context);
initialize();
}
public GroupShareProfileView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize();
}
public GroupShareProfileView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public GroupShareProfileView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
inflate(getContext(), R.layout.profile_group_share_view, this);
this.container = ViewUtil.findById(this, R.id.container);
this.container.setOnClickListener(view -> {
if (this.recipient != null) {
new AlertDialog.Builder(getContext())
.setIconAttribute(R.attr.dialog_info_icon)
.setTitle(R.string.GroupShareProfileView_share_your_profile_name_and_photo_with_this_group)
.setMessage(R.string.GroupShareProfileView_do_you_want_to_make_your_profile_name_and_photo_visible_to_all_current_and_future_members_of_this_group)
.setPositiveButton(R.string.GroupShareProfileView_make_visible, (dialog, which) -> {
DatabaseFactory.getRecipientDatabase(getContext()).setProfileSharing(recipient, true);
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
});
}
public void setRecipient(@NonNull Recipient recipient) {
this.recipient = recipient;
}
}

View File

@ -430,6 +430,9 @@ public class Recipient implements RecipientModifiedListener {
} }
public synchronized RegisteredState getRegistered() { public synchronized RegisteredState getRegistered() {
if (isPushGroupRecipient()) return RegisteredState.REGISTERED;
else if (isMmsGroupRecipient()) return RegisteredState.NOT_REGISTERED;
return registered; return registered;
} }