kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add Group Call peeking in the Conversation view.
rodzic
2729eb9f5f
commit
01f143667f
|
@ -25,6 +25,7 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ShortcutManager;
|
import android.content.pm.ShortcutManager;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
@ -82,6 +83,7 @@ import com.annimon.stream.Stream;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
import com.bumptech.glide.request.target.CustomTarget;
|
import com.bumptech.glide.request.target.CustomTarget;
|
||||||
import com.bumptech.glide.request.transition.Transition;
|
import com.bumptech.glide.request.transition.Transition;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
@ -135,6 +137,7 @@ import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationGroupViewModel.GroupActiveState;
|
import org.thoughtcrime.securesms.conversation.ConversationGroupViewModel.GroupActiveState;
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
|
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
|
||||||
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
|
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
|
||||||
|
import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel;
|
||||||
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
|
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
|
||||||
import org.thoughtcrime.securesms.conversationlist.model.MessageResult;
|
import org.thoughtcrime.securesms.conversationlist.model.MessageResult;
|
||||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||||
|
@ -159,6 +162,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
|
||||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||||
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
||||||
import org.thoughtcrime.securesms.groups.GroupChangeException;
|
import org.thoughtcrime.securesms.groups.GroupChangeException;
|
||||||
|
@ -369,6 +373,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
private View requestingMemberBanner;
|
private View requestingMemberBanner;
|
||||||
private View cancelJoinRequest;
|
private View cancelJoinRequest;
|
||||||
private Stub<View> mentionsSuggestions;
|
private Stub<View> mentionsSuggestions;
|
||||||
|
private MaterialButton joinGroupCallButton;
|
||||||
|
|
||||||
private LinkPreviewViewModel linkPreviewViewModel;
|
private LinkPreviewViewModel linkPreviewViewModel;
|
||||||
private ConversationSearchViewModel searchViewModel;
|
private ConversationSearchViewModel searchViewModel;
|
||||||
|
@ -377,6 +382,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
private InviteReminderModel inviteReminderModel;
|
private InviteReminderModel inviteReminderModel;
|
||||||
private ConversationGroupViewModel groupViewModel;
|
private ConversationGroupViewModel groupViewModel;
|
||||||
private MentionsPickerViewModel mentionsViewModel;
|
private MentionsPickerViewModel mentionsViewModel;
|
||||||
|
private GroupCallViewModel groupCallViewModel;
|
||||||
|
|
||||||
private LiveRecipient recipient;
|
private LiveRecipient recipient;
|
||||||
private long threadId;
|
private long threadId;
|
||||||
|
@ -426,6 +432,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
initializeViewModel(args);
|
initializeViewModel(args);
|
||||||
initializeGroupViewModel();
|
initializeGroupViewModel();
|
||||||
initializeMentionsViewModel();
|
initializeMentionsViewModel();
|
||||||
|
initializeGroupCallViewModel();
|
||||||
initializeEnabledCheck();
|
initializeEnabledCheck();
|
||||||
initializePendingRequestsBanner();
|
initializePendingRequestsBanner();
|
||||||
initializeGroupV1MigrationsBanners();
|
initializeGroupV1MigrationsBanners();
|
||||||
|
@ -532,6 +539,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
.enqueue();
|
.enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (groupCallViewModel != null) {
|
||||||
|
groupCallViewModel.peekGroupCall(this);
|
||||||
|
}
|
||||||
|
|
||||||
setVisibleThread(threadId);
|
setVisibleThread(threadId);
|
||||||
ConversationUtil.refreshRecipientShortcuts();
|
ConversationUtil.refreshRecipientShortcuts();
|
||||||
}
|
}
|
||||||
|
@ -816,6 +827,9 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
} else if (isGroupConversation()) {
|
} else if (isGroupConversation()) {
|
||||||
if (isActiveV2Group && FeatureFlags.groupCalling()) {
|
if (isActiveV2Group && FeatureFlags.groupCalling()) {
|
||||||
inflater.inflate(R.menu.conversation_callable_groupv2, menu);
|
inflater.inflate(R.menu.conversation_callable_groupv2, menu);
|
||||||
|
if (groupCallViewModel != null && Boolean.TRUE.equals(groupCallViewModel.hasActiveGroupCall().getValue())) {
|
||||||
|
hideMenuItem(menu, R.id.menu_video_secure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inflater.inflate(R.menu.conversation_group_options, menu);
|
inflater.inflate(R.menu.conversation_group_options, menu);
|
||||||
|
@ -1895,6 +1909,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
|
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
|
||||||
requestingMemberBanner = findViewById(R.id.conversation_requesting_banner);
|
requestingMemberBanner = findViewById(R.id.conversation_requesting_banner);
|
||||||
cancelJoinRequest = findViewById(R.id.conversation_cancel_request);
|
cancelJoinRequest = findViewById(R.id.conversation_cancel_request);
|
||||||
|
joinGroupCallButton = findViewById(R.id.conversation_group_cal_join);
|
||||||
|
|
||||||
container.addOnKeyboardShownListener(this);
|
container.addOnKeyboardShownListener(this);
|
||||||
inputPanel.setListener(this);
|
inputPanel.setListener(this);
|
||||||
|
@ -1949,6 +1964,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
|
inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
|
||||||
|
|
||||||
reactionOverlay.setOnReactionSelectedListener(this);
|
reactionOverlay.setOnReactionSelectedListener(this);
|
||||||
|
|
||||||
|
joinGroupCallButton.setOnClickListener(v -> handleVideo(getRecipient()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initializeActionBar() {
|
protected void initializeActionBar() {
|
||||||
|
@ -2105,6 +2122,21 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initializeGroupCallViewModel() {
|
||||||
|
groupCallViewModel = ViewModelProviders.of(this, new GroupCallViewModel.Factory()).get(GroupCallViewModel.class);
|
||||||
|
|
||||||
|
recipient.observe(this, r -> {
|
||||||
|
groupCallViewModel.onRecipientChange(this, r);
|
||||||
|
});
|
||||||
|
|
||||||
|
groupCallViewModel.hasActiveGroupCall().observe(this, hasActiveCall -> {
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
joinGroupCallButton.setVisibility(hasActiveCall ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
|
||||||
|
groupCallViewModel.canJoinGroupCall().observe(this, canJoin -> joinGroupCallButton.setText(canJoin ? R.string.ConversationActivity_join : R.string.ConversationActivity_full));
|
||||||
|
}
|
||||||
|
|
||||||
private void showStickerIntroductionTooltip() {
|
private void showStickerIntroductionTooltip() {
|
||||||
TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER);
|
TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER);
|
||||||
inputPanel.setMediaKeyboardToggleMode(true);
|
inputPanel.setMediaKeyboardToggleMode(true);
|
||||||
|
@ -2218,6 +2250,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
if (mentionsViewModel != null) {
|
if (mentionsViewModel != null) {
|
||||||
mentionsViewModel.onRecipientChange(recipient);
|
mentionsViewModel.onRecipientChange(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (groupCallViewModel != null) {
|
||||||
|
groupCallViewModel.onRecipientChange(this, recipient);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
@ -2239,6 +2275,13 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||||
|
public void onGroupCallPeekEvent(@NonNull GroupCallPeekEvent event) {
|
||||||
|
if (groupCallViewModel != null) {
|
||||||
|
groupCallViewModel.onGroupCallPeekEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeReceivers() {
|
private void initializeReceivers() {
|
||||||
securityUpdateReceiver = new BroadcastReceiver() {
|
securityUpdateReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -2396,8 +2439,13 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
private void setActionBarColor(MaterialColor color) {
|
private void setActionBarColor(MaterialColor color) {
|
||||||
ActionBar supportActionBar = getSupportActionBar();
|
ActionBar supportActionBar = getSupportActionBar();
|
||||||
if (supportActionBar == null) throw new AssertionError();
|
if (supportActionBar == null) throw new AssertionError();
|
||||||
supportActionBar.setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
|
int actionBarColor = color.toActionBarColor(this);
|
||||||
|
supportActionBar.setBackgroundDrawable(new ColorDrawable(actionBarColor));
|
||||||
WindowUtil.setStatusBarColor(getWindow(), color.toStatusBarColor(this));
|
WindowUtil.setStatusBarColor(getWindow(), color.toStatusBarColor(this));
|
||||||
|
|
||||||
|
joinGroupCallButton.setTextColor(actionBarColor);
|
||||||
|
joinGroupCallButton.setIconTint(ColorStateList.valueOf(actionBarColor));
|
||||||
|
joinGroupCallButton.setRippleColor(ColorStateList.valueOf(actionBarColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
|
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.BindableConversationItem;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.VerifyIdentityActivity;
|
import org.thoughtcrime.securesms.VerifyIdentityActivity;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
|
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil;
|
||||||
import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
|
import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.UpdateDescription;
|
import org.thoughtcrime.securesms.database.model.UpdateDescription;
|
||||||
|
@ -182,14 +183,17 @@ public final class ConversationUpdateItem extends LinearLayout
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (conversationMessage.getMessageRecord().isGroupCall()) {
|
} else if (conversationMessage.getMessageRecord().isGroupCall()) {
|
||||||
|
|
||||||
UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
|
UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
|
||||||
Collection<UUID> uuids = updateDescription.getMentioned();
|
Collection<UUID> uuids = updateDescription.getMentioned();
|
||||||
|
|
||||||
int text = 0;
|
int text = 0;
|
||||||
if (Util.hasItems(uuids)) {
|
if (Util.hasItems(uuids)) {
|
||||||
|
if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).getIsCallFull()) {
|
||||||
|
text = R.string.ConversationUpdateItem_call_is_full;
|
||||||
|
} else {
|
||||||
text = uuids.contains(TextSecurePreferences.getLocalUuid(getContext())) ? R.string.ConversationUpdateItem_return_to_call : R.string.ConversationUpdateItem_join_call;
|
text = uuids.contains(TextSecurePreferences.getLocalUuid(getContext())) ? R.string.ConversationUpdateItem_return_to_call : R.string.ConversationUpdateItem_join_call;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (text != 0) {
|
if (text != 0) {
|
||||||
actionButton.setText(text);
|
actionButton.setText(text);
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package org.thoughtcrime.securesms.conversation.ui.groupcall;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
import androidx.lifecycle.ViewModel;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||||
|
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||||
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class GroupCallViewModel extends ViewModel {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(GroupCallViewModel.class);
|
||||||
|
|
||||||
|
private final MutableLiveData<Boolean> activeGroupCall;
|
||||||
|
private final MutableLiveData<Boolean> canJoin;
|
||||||
|
|
||||||
|
private @Nullable Recipient currentRecipient;
|
||||||
|
|
||||||
|
GroupCallViewModel() {
|
||||||
|
this.activeGroupCall = new MutableLiveData<>(false);
|
||||||
|
this.canJoin = new MutableLiveData<>(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull LiveData<Boolean> hasActiveGroupCall() {
|
||||||
|
return activeGroupCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull LiveData<Boolean> canJoinGroupCall() {
|
||||||
|
return canJoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRecipientChange(@NonNull Context context, @Nullable Recipient recipient) {
|
||||||
|
if (Objects.equals(currentRecipient, recipient)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeGroupCall.postValue(false);
|
||||||
|
canJoin.postValue(false);
|
||||||
|
|
||||||
|
currentRecipient = recipient;
|
||||||
|
|
||||||
|
peekGroupCall(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void peekGroupCall(@NonNull Context context) {
|
||||||
|
if (isGroupCallCapable(currentRecipient)) {
|
||||||
|
Log.i(TAG, "peek call for " + currentRecipient.getId());
|
||||||
|
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||||
|
intent.setAction(WebRtcCallService.ACTION_GROUP_CALL_PEEK)
|
||||||
|
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(currentRecipient.getId()));
|
||||||
|
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGroupCallPeekEvent(@NonNull GroupCallPeekEvent groupCallPeekEvent) {
|
||||||
|
if (isGroupCallCapable(currentRecipient) && groupCallPeekEvent.getGroupRecipientId().equals(currentRecipient.getId())) {
|
||||||
|
Log.i(TAG, "update UI with call event: active call: " + groupCallPeekEvent.hasActiveCall() + " canJoin: " + groupCallPeekEvent.canJoinCall());
|
||||||
|
|
||||||
|
activeGroupCall.postValue(groupCallPeekEvent.hasActiveCall());
|
||||||
|
canJoin.postValue(groupCallPeekEvent.canJoinCall());
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Ignore call event for different recipient.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isGroupCallCapable(@Nullable Recipient recipient) {
|
||||||
|
return recipient != null && recipient.isActiveGroup() && recipient.isPushV2Group() && FeatureFlags.groupCalling();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Factory implements ViewModelProvider.Factory {
|
||||||
|
@Override
|
||||||
|
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
return modelClass.cast(new GroupCallViewModel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -133,12 +133,14 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||||
public abstract @NonNull Pair<Long, Long> insertReceivedCall(@NonNull RecipientId address, boolean isVideoOffer);
|
public abstract @NonNull Pair<Long, Long> insertReceivedCall(@NonNull RecipientId address, boolean isVideoOffer);
|
||||||
public abstract @NonNull Pair<Long, Long> insertOutgoingCall(@NonNull RecipientId address, boolean isVideoOffer);
|
public abstract @NonNull Pair<Long, Long> insertOutgoingCall(@NonNull RecipientId address, boolean isVideoOffer);
|
||||||
public abstract @NonNull Pair<Long, Long> insertMissedCall(@NonNull RecipientId address, long timestamp, boolean isVideoOffer);
|
public abstract @NonNull Pair<Long, Long> insertMissedCall(@NonNull RecipientId address, long timestamp, boolean isVideoOffer);
|
||||||
public abstract @NonNull void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
public abstract void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||||
@NonNull RecipientId sender,
|
@NonNull RecipientId sender,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
@Nullable String messageGroupCallEraId,
|
@Nullable String messageGroupCallEraId,
|
||||||
@Nullable String peekGroupCallEraId,
|
@Nullable String peekGroupCallEraId,
|
||||||
@NonNull Collection<UUID> peekJoinedUuids);
|
@NonNull Collection<UUID> peekJoinedUuids,
|
||||||
|
boolean isCallFull);
|
||||||
|
public abstract boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull);
|
||||||
|
|
||||||
public abstract Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type);
|
public abstract Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type);
|
||||||
public abstract Optional<InsertResult> insertMessageInbox(IncomingTextMessage message);
|
public abstract Optional<InsertResult> insertMessageInbox(IncomingTextMessage message);
|
||||||
|
|
|
@ -464,16 +464,22 @@ public class MmsDatabase extends MessageDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||||
@NonNull RecipientId sender,
|
@NonNull RecipientId sender,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
@Nullable String messageGroupCallEraId,
|
@Nullable String messageGroupCallEraId,
|
||||||
@Nullable String peekGroupCallEraId,
|
@Nullable String peekGroupCallEraId,
|
||||||
@NonNull Collection<UUID> peekJoinedUuids)
|
@NonNull Collection<UUID> peekJoinedUuids,
|
||||||
|
boolean isCallFull)
|
||||||
{
|
{
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type) {
|
public Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
@ -691,7 +691,8 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
long timestamp,
|
long timestamp,
|
||||||
@Nullable String messageGroupCallEraId,
|
@Nullable String messageGroupCallEraId,
|
||||||
@Nullable String peekGroupCallEraId,
|
@Nullable String peekGroupCallEraId,
|
||||||
@NonNull Collection<UUID> peekJoinedUuids)
|
@NonNull Collection<UUID> peekJoinedUuids,
|
||||||
|
boolean isCallFull)
|
||||||
{
|
{
|
||||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
|
@ -701,7 +702,7 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
Recipient recipient = Recipient.resolved(groupRecipientId);
|
Recipient recipient = Recipient.resolved(groupRecipientId);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||||
|
|
||||||
boolean peerEraIdSameAsPrevious = updatePreviousGroupCall(threadId, peekGroupCallEraId, peekJoinedUuids);
|
boolean peerEraIdSameAsPrevious = updatePreviousGroupCall(threadId, peekGroupCallEraId, peekJoinedUuids, isCallFull);
|
||||||
|
|
||||||
if (!peerEraIdSameAsPrevious && !Util.isEmpty(peekGroupCallEraId)) {
|
if (!peerEraIdSameAsPrevious && !Util.isEmpty(peekGroupCallEraId)) {
|
||||||
Recipient self = Recipient.self();
|
Recipient self = Recipient.self();
|
||||||
|
@ -712,6 +713,7 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
.setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString())
|
.setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString())
|
||||||
.setStartedCallTimestamp(timestamp)
|
.setStartedCallTimestamp(timestamp)
|
||||||
.addAllInCallUuids(Stream.of(peekJoinedUuids).map(UUID::toString).toList())
|
.addAllInCallUuids(Stream.of(peekJoinedUuids).map(UUID::toString).toList())
|
||||||
|
.setIsCallFull(isCallFull)
|
||||||
.build()
|
.build()
|
||||||
.toByteArray();
|
.toByteArray();
|
||||||
|
|
||||||
|
@ -743,7 +745,8 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids) {
|
@Override
|
||||||
|
public boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull) {
|
||||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
String where = TYPE + " = ? AND " + THREAD_ID + " = ?";
|
String where = TYPE + " = ? AND " + THREAD_ID + " = ?";
|
||||||
String[] args = SqlUtil.buildArgs(Types.GROUP_CALL_TYPE, threadId);
|
String[] args = SqlUtil.buildArgs(Types.GROUP_CALL_TYPE, threadId);
|
||||||
|
@ -761,7 +764,7 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
List<String> inCallUuids = sameEraId ? Stream.of(peekJoinedUuids).map(UUID::toString).toList()
|
List<String> inCallUuids = sameEraId ? Stream.of(peekJoinedUuids).map(UUID::toString).toList()
|
||||||
: Collections.emptyList();
|
: Collections.emptyList();
|
||||||
|
|
||||||
String body = GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, inCallUuids);
|
String body = GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, inCallUuids, isCallFull);
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(BODY, body);
|
contentValues.put(BODY, body);
|
||||||
|
|
|
@ -34,8 +34,9 @@ public final class GroupCallUpdateDetailsUtil {
|
||||||
return groupCallUpdateDetails;
|
return groupCallUpdateDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull String createUpdatedBody(@NonNull GroupCallUpdateDetails groupCallUpdateDetails, @NonNull List<String> inCallUuids) {
|
public static @NonNull String createUpdatedBody(@NonNull GroupCallUpdateDetails groupCallUpdateDetails, @NonNull List<String> inCallUuids, boolean isCallFull) {
|
||||||
GroupCallUpdateDetails.Builder builder = groupCallUpdateDetails.toBuilder()
|
GroupCallUpdateDetails.Builder builder = groupCallUpdateDetails.toBuilder()
|
||||||
|
.setIsCallFull(isCallFull)
|
||||||
.clearInCallUuids();
|
.clearInCallUuids();
|
||||||
|
|
||||||
if (Util.hasItems(inCallUuids)) {
|
if (Util.hasItems(inCallUuids)) {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package org.thoughtcrime.securesms.events;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
|
||||||
|
public final class GroupCallPeekEvent {
|
||||||
|
private final RecipientId groupRecipientId;
|
||||||
|
private final String eraId;
|
||||||
|
private final long deviceCount;
|
||||||
|
private final long deviceLimit;
|
||||||
|
|
||||||
|
public GroupCallPeekEvent(@NonNull RecipientId groupRecipientId, @Nullable String eraId, @Nullable Long deviceCount, @Nullable Long deviceLimit) {
|
||||||
|
this.groupRecipientId = groupRecipientId;
|
||||||
|
this.eraId = eraId;
|
||||||
|
this.deviceCount = deviceCount != null ? deviceCount : 0;
|
||||||
|
this.deviceLimit = deviceLimit != null ? deviceLimit : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull RecipientId getGroupRecipientId() {
|
||||||
|
return groupRecipientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasActiveCall() {
|
||||||
|
return eraId != null && deviceCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canJoinCall() {
|
||||||
|
return hasActiveCall() && deviceCount < deviceLimit;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
|
||||||
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||||
|
@ -52,6 +53,7 @@ import org.thoughtcrime.securesms.ringrtc.TurnServerInfoParcel;
|
||||||
import org.thoughtcrime.securesms.service.webrtc.IdleActionProcessor;
|
import org.thoughtcrime.securesms.service.webrtc.IdleActionProcessor;
|
||||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData;
|
import org.thoughtcrime.securesms.service.webrtc.WebRtcData;
|
||||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcInteractor;
|
import org.thoughtcrime.securesms.service.webrtc.WebRtcInteractor;
|
||||||
|
import org.thoughtcrime.securesms.service.webrtc.WebRtcUtil;
|
||||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
||||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||||
|
@ -200,6 +202,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
||||||
public static final String ACTION_GROUP_UPDATE_RENDERED_RESOLUTIONS = "GROUP_UPDATE_RENDERED_RESOLUTIONS";
|
public static final String ACTION_GROUP_UPDATE_RENDERED_RESOLUTIONS = "GROUP_UPDATE_RENDERED_RESOLUTIONS";
|
||||||
public static final String ACTION_GROUP_CALL_ENDED = "GROUP_CALL_ENDED";
|
public static final String ACTION_GROUP_CALL_ENDED = "GROUP_CALL_ENDED";
|
||||||
public static final String ACTION_GROUP_CALL_UPDATE_MESSAGE = "GROUP_CALL_UPDATE_MESSAGE";
|
public static final String ACTION_GROUP_CALL_UPDATE_MESSAGE = "GROUP_CALL_UPDATE_MESSAGE";
|
||||||
|
public static final String ACTION_GROUP_CALL_PEEK = "GROUP_CALL_PEEK";
|
||||||
|
|
||||||
public static final int BUSY_TONE_LENGTH = 2000;
|
public static final int BUSY_TONE_LENGTH = 2000;
|
||||||
|
|
||||||
|
@ -686,25 +689,59 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
||||||
groupCallUpdateMetadata.getServerReceivedTimestamp(),
|
groupCallUpdateMetadata.getServerReceivedTimestamp(),
|
||||||
groupCallUpdateMetadata.getGroupCallEraId(),
|
groupCallUpdateMetadata.getGroupCallEraId(),
|
||||||
peekInfo.getEraId(),
|
peekInfo.getEraId(),
|
||||||
peekInfo.getJoinedMembers());
|
peekInfo.getJoinedMembers(),
|
||||||
|
WebRtcUtil.isCallFull(peekInfo));
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(group);
|
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(group);
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(this, threadId, true);
|
ApplicationDependencies.getMessageNotifier().updateNotification(this, threadId, true);
|
||||||
|
|
||||||
|
EventBus.getDefault().postSticky(new GroupCallPeekEvent(group.getId(), peekInfo.getEraId(), peekInfo.getDeviceCount(), peekInfo.getMaxDevices()));
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (IOException | VerificationFailedException | CallException e) {
|
} catch (IOException | VerificationFailedException | CallException e) {
|
||||||
Log.e(TAG, "error peeking", e);
|
Log.e(TAG, "error peeking from message", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers) {
|
public void peekGroupCall(@NonNull RecipientId id) {
|
||||||
|
networkExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
Recipient group = Recipient.resolved(id);
|
||||||
|
GroupId.V2 groupId = group.requireGroupId().requireV2();
|
||||||
|
GroupExternalCredential credential = GroupManager.getGroupExternalCredential(this, groupId);
|
||||||
|
|
||||||
|
List<GroupCall.GroupMemberInfo> members = Stream.of(GroupManager.getUuidCipherTexts(this, groupId))
|
||||||
|
.map(entry -> new GroupCall.GroupMemberInfo(entry.getKey(), entry.getValue().serialize()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
callManager.peekGroupCall(BuildConfig.SIGNAL_SFU_URL, credential.getTokenBytes().toByteArray(), members, peekInfo -> {
|
||||||
|
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(group);
|
||||||
|
|
||||||
|
DatabaseFactory.getSmsDatabase(this).updatePreviousGroupCall(threadId,
|
||||||
|
peekInfo.getEraId(),
|
||||||
|
peekInfo.getJoinedMembers(),
|
||||||
|
WebRtcUtil.isCallFull(peekInfo));
|
||||||
|
|
||||||
|
ApplicationDependencies.getMessageNotifier().updateNotification(this, threadId, true);
|
||||||
|
|
||||||
|
EventBus.getDefault().postSticky(new GroupCallPeekEvent(id, peekInfo.getEraId(), peekInfo.getDeviceCount(), peekInfo.getMaxDevices()));
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (IOException | VerificationFailedException | CallException e) {
|
||||||
|
Log.e(TAG, "error peeking from active conversation", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers, boolean isCallFull) {
|
||||||
DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId,
|
DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId,
|
||||||
Recipient.self().getId(),
|
Recipient.self().getId(),
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
null,
|
null,
|
||||||
groupCallEraId,
|
groupCallEraId,
|
||||||
joinedMembers);
|
joinedMembers,
|
||||||
|
isCallFull);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -125,7 +125,7 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
|
||||||
if (!members.contains(Recipient.self().requireUuid())) {
|
if (!members.contains(Recipient.self().requireUuid())) {
|
||||||
members.add(Recipient.self().requireUuid());
|
members.add(Recipient.self().requireUuid());
|
||||||
}
|
}
|
||||||
webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members);
|
webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members, WebRtcUtil.isCallFull(peekInfo));
|
||||||
|
|
||||||
return currentState.builder()
|
return currentState.builder()
|
||||||
.changeCallSetupState()
|
.changeCallSetupState()
|
||||||
|
@ -149,7 +149,7 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
|
||||||
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId);
|
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId);
|
||||||
|
|
||||||
List<UUID> members = Stream.of(currentState.getCallInfoState().getRemoteCallParticipants()).map(p -> p.getRecipient().requireUuid()).toList();
|
List<UUID> members = Stream.of(currentState.getCallInfoState().getRemoteCallParticipants()).map(p -> p.getRecipient().requireUuid()).toList();
|
||||||
webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members);
|
webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members, false);
|
||||||
|
|
||||||
currentState = currentState.builder()
|
currentState = currentState.builder()
|
||||||
.changeCallInfoState()
|
.changeCallInfoState()
|
||||||
|
|
|
@ -61,6 +61,7 @@ import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_
|
||||||
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_TIMEOUT;
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_TIMEOUT;
|
||||||
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_FLIP_CAMERA;
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_FLIP_CAMERA;
|
||||||
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_ENDED;
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_ENDED;
|
||||||
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_PEEK;
|
||||||
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_UPDATE_MESSAGE;
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_UPDATE_MESSAGE;
|
||||||
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_JOINED_MEMBERSHIP_CHANGED;
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_JOINED_MEMBERSHIP_CHANGED;
|
||||||
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_LOCAL_DEVICE_STATE_CHANGED;
|
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_LOCAL_DEVICE_STATE_CHANGED;
|
||||||
|
@ -238,6 +239,7 @@ public abstract class WebRtcActionProcessor {
|
||||||
case ACTION_GROUP_UPDATE_RENDERED_RESOLUTIONS: return handleUpdateRenderedResolutions(currentState);
|
case ACTION_GROUP_UPDATE_RENDERED_RESOLUTIONS: return handleUpdateRenderedResolutions(currentState);
|
||||||
case ACTION_GROUP_CALL_ENDED: return handleGroupCallEnded(currentState, getGroupCallHash(intent), getGroupCallEndReason(intent));
|
case ACTION_GROUP_CALL_ENDED: return handleGroupCallEnded(currentState, getGroupCallHash(intent), getGroupCallEndReason(intent));
|
||||||
case ACTION_GROUP_CALL_UPDATE_MESSAGE: return handleGroupCallUpdateMessage(currentState, GroupCallUpdateMetadata.fromIntent(intent));
|
case ACTION_GROUP_CALL_UPDATE_MESSAGE: return handleGroupCallUpdateMessage(currentState, GroupCallUpdateMetadata.fromIntent(intent));
|
||||||
|
case ACTION_GROUP_CALL_PEEK: return handleGroupCallPeek(currentState, getRemotePeer(intent));
|
||||||
|
|
||||||
case ACTION_HTTP_SUCCESS: return handleHttpSuccess(currentState, HttpData.fromIntent(intent));
|
case ACTION_HTTP_SUCCESS: return handleHttpSuccess(currentState, HttpData.fromIntent(intent));
|
||||||
case ACTION_HTTP_FAILURE: return handleHttpFailure(currentState, HttpData.fromIntent(intent));
|
case ACTION_HTTP_FAILURE: return handleHttpFailure(currentState, HttpData.fromIntent(intent));
|
||||||
|
@ -727,6 +729,11 @@ public abstract class WebRtcActionProcessor {
|
||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected @NonNull WebRtcServiceState handleGroupCallPeek(@NonNull WebRtcServiceState currentState, @NonNull RemotePeer remotePeer) {
|
||||||
|
webRtcInteractor.peekGroupCall(remotePeer.getId());
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
protected @NonNull WebRtcServiceState handleHttpSuccess(@NonNull WebRtcServiceState currentState, @NonNull HttpData httpData) {
|
protected @NonNull WebRtcServiceState handleHttpSuccess(@NonNull WebRtcServiceState currentState, @NonNull HttpData httpData) {
|
||||||
|
|
|
@ -94,8 +94,8 @@ public class WebRtcInteractor {
|
||||||
webRtcCallService.sendGroupCallMessage(recipient, groupCallEraId);
|
webRtcCallService.sendGroupCallMessage(recipient, groupCallEraId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers) {
|
void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers, boolean isCallFull) {
|
||||||
webRtcCallService.updateGroupCallUpdateMessage(groupId, groupCallEraId, joinedMembers);
|
webRtcCallService.updateGroupCallUpdateMessage(groupId, groupCallEraId, joinedMembers, isCallFull);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer) {
|
void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer) {
|
||||||
|
@ -157,4 +157,8 @@ public class WebRtcInteractor {
|
||||||
void peekGroupCall(@NonNull WebRtcData.GroupCallUpdateMetadata groupCallUpdateMetadata) {
|
void peekGroupCall(@NonNull WebRtcData.GroupCallUpdateMetadata groupCallUpdateMetadata) {
|
||||||
webRtcCallService.peekGroupCall(groupCallUpdateMetadata);
|
webRtcCallService.peekGroupCall(groupCallUpdateMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void peekGroupCall(@NonNull RecipientId recipientId) {
|
||||||
|
webRtcCallService.peekGroupCall(recipientId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,4 +77,8 @@ public final class WebRtcUtil {
|
||||||
PeekInfo peekInfo = groupCall.getPeekInfo();
|
PeekInfo peekInfo = groupCall.getPeekInfo();
|
||||||
return peekInfo != null ? peekInfo.getEraId() : null;
|
return peekInfo != null ? peekInfo.getEraId() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isCallFull(@Nullable PeekInfo peekInfo) {
|
||||||
|
return peekInfo != null && peekInfo.getMaxDevices() != null && peekInfo.getDeviceCount() >= peekInfo.getMaxDevices();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,4 +75,5 @@ message GroupCallUpdateDetails {
|
||||||
string startedCallUuid = 2;
|
string startedCallUuid = 2;
|
||||||
int64 startedCallTimestamp = 3;
|
int64 startedCallTimestamp = 3;
|
||||||
repeated string inCallUuids = 4;
|
repeated string inCallUuids = 4;
|
||||||
|
bool isCallFull = 5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M23,8.563v6.874a1,1 0,0 1,-1.419 0.908L18.5,14.923V9.077l3.081,-1.422A1,1 0,0 1,23 8.563ZM2.535,4.876A2.639,2.639 0,0 0,1.4 5.939,3.854 3.854,0 0,0 1,8.105V15.9a3.854,3.854 0,0 0,0.4 2.166,2.639 2.639,0 0,0 1.134,1.063 4.6,4.6 0,0 0,2.311 0.376h8.308a4.6,4.6 0,0 0,2.311 -0.376A2.639,2.639 0,0 0,16.6 18.061,3.854 3.854,0 0,0 17,15.9V8.105a3.854,3.854 0,0 0,-0.4 -2.166,2.639 2.639,0 0,0 -1.134,-1.063A4.6,4.6 0,0 0,13.154 4.5H4.846A4.6,4.6 0,0 0,2.535 4.876Z"/>
|
||||||
|
</vector>
|
|
@ -22,7 +22,33 @@
|
||||||
app:contentInsetStart="46dp"
|
app:contentInsetStart="46dp"
|
||||||
tools:background="#ff007f00">
|
tools:background="#ff007f00">
|
||||||
|
|
||||||
<include layout="@layout/conversation_title_view" />
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<include layout="@layout/conversation_title_view"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/conversation_group_cal_join"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:text="@string/ConversationActivity_join"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:backgroundTint="@color/core_white"
|
||||||
|
app:cornerRadius="1000dp"
|
||||||
|
app:icon="@drawable/ic_video_solid_18"
|
||||||
|
app:iconGravity="textStart"
|
||||||
|
tools:iconTint="@color/core_ultramarine"
|
||||||
|
tools:textColor="@color/core_ultramarine"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
tools:background="#007fff">
|
tools:background="#007fff"
|
||||||
|
tools:layout_height="?actionBarSize">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||||
android:id="@+id/contact_photo_image"
|
android:id="@+id/contact_photo_image"
|
||||||
|
|
|
@ -298,6 +298,9 @@
|
||||||
|
|
||||||
<string name="ConversationActivity__more_options_now_in_group_settings">More options now in \"Group settings\"</string>
|
<string name="ConversationActivity__more_options_now_in_group_settings">More options now in \"Group settings\"</string>
|
||||||
|
|
||||||
|
<string name="ConversationActivity_join">Join</string>
|
||||||
|
<string name="ConversationActivity_full">Full</string>
|
||||||
|
|
||||||
<!-- ConversationAdapter -->
|
<!-- ConversationAdapter -->
|
||||||
<plurals name="ConversationAdapter_n_unread_messages">
|
<plurals name="ConversationAdapter_n_unread_messages">
|
||||||
<item quantity="one">%d unread message</item>
|
<item quantity="one">%d unread message</item>
|
||||||
|
@ -1924,6 +1927,7 @@
|
||||||
<string name="ConversationUpdateItem_learn_more">Learn more</string>
|
<string name="ConversationUpdateItem_learn_more">Learn more</string>
|
||||||
<string name="ConversationUpdateItem_join_call">Join call</string>
|
<string name="ConversationUpdateItem_join_call">Join call</string>
|
||||||
<string name="ConversationUpdateItem_return_to_call">Return to call</string>
|
<string name="ConversationUpdateItem_return_to_call">Return to call</string>
|
||||||
|
<string name="ConversationUpdateItem_call_is_full">Call is full</string>
|
||||||
|
|
||||||
<!-- audio_view -->
|
<!-- audio_view -->
|
||||||
<string name="audio_view__play_pause_accessibility_description">Play … Pause</string>
|
<string name="audio_view__play_pause_accessibility_description">Play … Pause</string>
|
||||||
|
|
Ładowanie…
Reference in New Issue