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.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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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> 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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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.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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,4 +75,5 @@ message GroupCallUpdateDetails {
|
|||
string startedCallUuid = 2;
|
||||
int64 startedCallTimestamp = 3;
|
||||
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"
|
||||
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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Ładowanie…
Reference in New Issue