Add Group Call peeking in the Conversation view.

fork-5.53.8
Cody Henthorne 2020-12-02 13:20:38 -05:00 zatwierdzone przez Greyson Parrelli
rodzic 2729eb9f5f
commit 01f143667f
18 zmienionych plików z 309 dodań i 30 usunięć

Wyświetl plik

@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
@ -82,6 +83,7 @@ import com.annimon.stream.Stream;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.button.MaterialButton;
import org.greenrobot.eventbus.EventBus;
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.ConversationMessage.ConversationMessageFactory;
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.conversationlist.model.MessageResult;
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.StickerRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.groups.GroupChangeException;
@ -369,6 +373,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
private View requestingMemberBanner;
private View cancelJoinRequest;
private Stub<View> mentionsSuggestions;
private MaterialButton joinGroupCallButton;
private LinkPreviewViewModel linkPreviewViewModel;
private ConversationSearchViewModel searchViewModel;
@ -377,6 +382,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
private InviteReminderModel inviteReminderModel;
private ConversationGroupViewModel groupViewModel;
private MentionsPickerViewModel mentionsViewModel;
private GroupCallViewModel groupCallViewModel;
private LiveRecipient recipient;
private long threadId;
@ -426,6 +432,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
initializeViewModel(args);
initializeGroupViewModel();
initializeMentionsViewModel();
initializeGroupCallViewModel();
initializeEnabledCheck();
initializePendingRequestsBanner();
initializeGroupV1MigrationsBanners();
@ -532,6 +539,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
.enqueue();
}
if (groupCallViewModel != null) {
groupCallViewModel.peekGroupCall(this);
}
setVisibleThread(threadId);
ConversationUtil.refreshRecipientShortcuts();
}
@ -816,6 +827,9 @@ public class ConversationActivity extends PassphraseRequiredActivity
} else if (isGroupConversation()) {
if (isActiveV2Group && FeatureFlags.groupCalling()) {
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);
@ -1895,6 +1909,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
requestingMemberBanner = findViewById(R.id.conversation_requesting_banner);
cancelJoinRequest = findViewById(R.id.conversation_cancel_request);
joinGroupCallButton = findViewById(R.id.conversation_group_cal_join);
container.addOnKeyboardShownListener(this);
inputPanel.setListener(this);
@ -1949,6 +1964,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
reactionOverlay.setOnReactionSelectedListener(this);
joinGroupCallButton.setOnClickListener(v -> handleVideo(getRecipient()));
}
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() {
TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER);
inputPanel.setMediaKeyboardToggleMode(true);
@ -2218,6 +2250,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
if (mentionsViewModel != null) {
mentionsViewModel.onRecipientChange(recipient);
}
if (groupCallViewModel != null) {
groupCallViewModel.onRecipientChange(this, recipient);
}
}
@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() {
securityUpdateReceiver = new BroadcastReceiver() {
@Override
@ -2396,8 +2439,13 @@ public class ConversationActivity extends PassphraseRequiredActivity
private void setActionBarColor(MaterialColor color) {
ActionBar supportActionBar = getSupportActionBar();
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));
joinGroupCallButton.setTextColor(actionBarColor);
joinGroupCallButton.setIconTint(ColorStateList.valueOf(actionBarColor));
joinGroupCallButton.setRippleColor(ColorStateList.valueOf(actionBarColor));
}
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {

Wyświetl plik

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.VerifyIdentityActivity;
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.MessageRecord;
import org.thoughtcrime.securesms.database.model.UpdateDescription;
@ -182,13 +183,16 @@ public final class ConversationUpdateItem extends LinearLayout
}
});
} else if (conversationMessage.getMessageRecord().isGroupCall()) {
UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
Collection<UUID> uuids = updateDescription.getMentioned();
int text = 0;
if (Util.hasItems(uuids)) {
text = uuids.contains(TextSecurePreferences.getLocalUuid(getContext())) ? R.string.ConversationUpdateItem_return_to_call : R.string.ConversationUpdateItem_join_call;
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;
}
}
if (text != 0) {

Wyświetl plik

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

Wyświetl plik

@ -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> insertOutgoingCall(@NonNull RecipientId address, boolean isVideoOffer);
public abstract @NonNull Pair<Long, Long> insertMissedCall(@NonNull RecipientId address, long timestamp, boolean isVideoOffer);
public abstract @NonNull void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
@NonNull RecipientId sender,
long timestamp,
@Nullable String messageGroupCallEraId,
@Nullable String peekGroupCallEraId,
@NonNull Collection<UUID> peekJoinedUuids);
public abstract void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
@NonNull RecipientId sender,
long timestamp,
@Nullable String messageGroupCallEraId,
@Nullable String peekGroupCallEraId,
@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);

Wyświetl plik

@ -464,16 +464,22 @@ public class MmsDatabase extends MessageDatabase {
}
@Override
public @NonNull void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
@NonNull RecipientId sender,
long timestamp,
@Nullable String messageGroupCallEraId,
@Nullable String peekGroupCallEraId,
@NonNull Collection<UUID> peekJoinedUuids)
public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
@NonNull RecipientId sender,
long timestamp,
@Nullable String messageGroupCallEraId,
@Nullable String peekGroupCallEraId,
@NonNull Collection<UUID> peekJoinedUuids,
boolean isCallFull)
{
throw new UnsupportedOperationException();
}
@Override
public boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull) {
throw new UnsupportedOperationException();
}
@Override
public Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type) {
throw new UnsupportedOperationException();

Wyświetl plik

@ -691,7 +691,8 @@ public class SmsDatabase extends MessageDatabase {
long timestamp,
@Nullable String messageGroupCallEraId,
@Nullable String peekGroupCallEraId,
@NonNull Collection<UUID> peekJoinedUuids)
@NonNull Collection<UUID> peekJoinedUuids,
boolean isCallFull)
{
SQLiteDatabase db = databaseHelper.getWritableDatabase();
@ -701,7 +702,7 @@ public class SmsDatabase extends MessageDatabase {
Recipient recipient = Recipient.resolved(groupRecipientId);
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)) {
Recipient self = Recipient.self();
@ -712,6 +713,7 @@ public class SmsDatabase extends MessageDatabase {
.setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString())
.setStartedCallTimestamp(timestamp)
.addAllInCallUuids(Stream.of(peekJoinedUuids).map(UUID::toString).toList())
.setIsCallFull(isCallFull)
.build()
.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();
String where = TYPE + " = ? AND " + THREAD_ID + " = ?";
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()
: Collections.emptyList();
String body = GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, inCallUuids);
String body = GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, inCallUuids, isCallFull);
ContentValues contentValues = new ContentValues();
contentValues.put(BODY, body);

Wyświetl plik

@ -34,8 +34,9 @@ public final class GroupCallUpdateDetailsUtil {
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()
.setIsCallFull(isCallFull)
.clearInCallUuids();
if (Util.hasItems(inCallUuids)) {

Wyświetl plik

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

Wyświetl plik

@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.groups.GroupId;
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.WebRtcData;
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.util.FutureTaskListener;
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_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_PEEK = "GROUP_CALL_PEEK";
public static final int BUSY_TONE_LENGTH = 2000;
@ -686,25 +689,59 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
groupCallUpdateMetadata.getServerReceivedTimestamp(),
groupCallUpdateMetadata.getGroupCallEraId(),
peekInfo.getEraId(),
peekInfo.getJoinedMembers());
peekInfo.getJoinedMembers(),
WebRtcUtil.isCallFull(peekInfo));
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(group);
ApplicationDependencies.getMessageNotifier().updateNotification(this, threadId, true);
EventBus.getDefault().postSticky(new GroupCallPeekEvent(group.getId(), peekInfo.getEraId(), peekInfo.getDeviceCount(), peekInfo.getMaxDevices()));
});
} 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,
Recipient.self().getId(),
System.currentTimeMillis(),
null,
groupCallEraId,
joinedMembers);
joinedMembers,
isCallFull);
}
@Override

Wyświetl plik

@ -125,7 +125,7 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
if (!members.contains(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()
.changeCallSetupState()
@ -149,7 +149,7 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId);
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()
.changeCallInfoState()

Wyświetl plik

@ -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_FLIP_CAMERA;
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_JOINED_MEMBERSHIP_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_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_PEEK: return handleGroupCallPeek(currentState, getRemotePeer(intent));
case ACTION_HTTP_SUCCESS: return handleHttpSuccess(currentState, HttpData.fromIntent(intent));
case ACTION_HTTP_FAILURE: return handleHttpFailure(currentState, HttpData.fromIntent(intent));
@ -727,6 +729,11 @@ public abstract class WebRtcActionProcessor {
return currentState;
}
protected @NonNull WebRtcServiceState handleGroupCallPeek(@NonNull WebRtcServiceState currentState, @NonNull RemotePeer remotePeer) {
webRtcInteractor.peekGroupCall(remotePeer.getId());
return currentState;
}
//endregion
protected @NonNull WebRtcServiceState handleHttpSuccess(@NonNull WebRtcServiceState currentState, @NonNull HttpData httpData) {

Wyświetl plik

@ -94,8 +94,8 @@ public class WebRtcInteractor {
webRtcCallService.sendGroupCallMessage(recipient, groupCallEraId);
}
void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers) {
webRtcCallService.updateGroupCallUpdateMessage(groupId, groupCallEraId, joinedMembers);
void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers, boolean isCallFull) {
webRtcCallService.updateGroupCallUpdateMessage(groupId, groupCallEraId, joinedMembers, isCallFull);
}
void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer) {
@ -157,4 +157,8 @@ public class WebRtcInteractor {
void peekGroupCall(@NonNull WebRtcData.GroupCallUpdateMetadata groupCallUpdateMetadata) {
webRtcCallService.peekGroupCall(groupCallUpdateMetadata);
}
void peekGroupCall(@NonNull RecipientId recipientId) {
webRtcCallService.peekGroupCall(recipientId);
}
}

Wyświetl plik

@ -77,4 +77,8 @@ public final class WebRtcUtil {
PeekInfo peekInfo = groupCall.getPeekInfo();
return peekInfo != null ? peekInfo.getEraId() : null;
}
public static boolean isCallFull(@Nullable PeekInfo peekInfo) {
return peekInfo != null && peekInfo.getMaxDevices() != null && peekInfo.getDeviceCount() >= peekInfo.getMaxDevices();
}
}

Wyświetl plik

@ -75,4 +75,5 @@ message GroupCallUpdateDetails {
string startedCallUuid = 2;
int64 startedCallTimestamp = 3;
repeated string inCallUuids = 4;
bool isCallFull = 5;
}

Wyświetl plik

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

Wyświetl plik

@ -22,7 +22,33 @@
app:contentInsetStart="46dp"
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>
</com.google.android.material.appbar.AppBarLayout>

Wyświetl plik

@ -7,7 +7,8 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
tools:background="#007fff">
tools:background="#007fff"
tools:layout_height="?actionBarSize">
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/contact_photo_image"

Wyświetl plik

@ -298,6 +298,9 @@
<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 -->
<plurals name="ConversationAdapter_n_unread_messages">
<item quantity="one">%d unread message</item>
@ -1924,6 +1927,7 @@
<string name="ConversationUpdateItem_learn_more">Learn more</string>
<string name="ConversationUpdateItem_join_call">Join call</string>
<string name="ConversationUpdateItem_return_to_call">Return to call</string>
<string name="ConversationUpdateItem_call_is_full">Call is full</string>
<!-- audio_view -->
<string name="audio_view__play_pause_accessibility_description">Play … Pause</string>